с использованием SSIS 2012. Мой диспетчер соединений с плоскими файлами У меня есть файл с разделителями, в котором разделитель строк установлен на CRLF
, но когда он обрабатывает файл, у меня есть текстовый столбец, в котором есть LF
. Это заставляет его читать это как терминатор строки, вызывающий сбой. Любые идеи?
SSIS читает LF как терминатор, когда он установлен как CRLF
Ответы (5)
У меня нет опыта работы с SSIS, но как разработчик ETL я сталкивался с этим много раз. Так что мои предложения могут не помочь вам решить проблему, но, надеюсь, укажут вам в правильном направлении.
- Если проблемное поле имеет текстовый квалификатор (обычно одинарные или двойные кавычки) и SSIS поддерживает его, используйте
- Также, если есть возможность заставить SSIS использовать другой разделитель конца записи, отличный от LF (CRLF в этом случае), я бы использовал его (надеюсь, в тексте поля проблемы нет CRLF)
- Если проблемное поле не является последним полем, вы можете подсчитать количество ограничителей, прочитав всю запись как одно поле с разделителями LF, чтобы идентифицировать и отфильтровать проблемные записи (если их всего несколько) и попытаться сшить их назад
- Если возможно, прочтите файл как одну запись (если у SSIS есть опция) и замените все LF, при условии, что CR соответствует конечному разделителю записи от источника
Прежде чем ответить, я не думаю, что столбец содержит только LF
, потому что, если разделитель строк равен CRLF
, он не будет рассматривать его как разделитель. Так что, вероятно, CRLF
, но я дам решение для двух случаев (CRLF или LF)
Решение
Вы можете исправить эту ситуацию, выполнив следующие действия:
- Сначала в диспетчере соединений с неструктурированными файлами добавьте только один столбец (типа
DT_STR
и длины4000
), так что вы будете рассматривать каждую строку как один столбец. - В задачу потока данных вы должны добавить компонент Script, который фиксирует файловую структуру. и разделите строку на столбцы.
Простой тест
Я буду рассматривать плоский файл со следующим содержанием
ID;name;DOB;Notes;ClassID{CRLF}
1;John;2001-01-01;;1{CRLF}
2;Moh;2002-01-01;Very cool{LF}
Genius;2{CRLF}
3;Ali;2000-01-01;Calm;2{CRLF}
- First i will add a flat file connection manager with the following options:
- Row Delimiter = {CRLF}
- Разделитель строки заголовка = {CRLF}
В задаче DataFlow я добавлю
Flat File Source
, 2 xScript Component
,OLEDB Destination
В первом компоненте скрипта я помечу
Column0
как вход, и я добавлю 5 выходных столбцовID,Name,DOB,Notes,ClassID
, и я установлю выходной синхронный вход какNone
В первом компоненте сценария я напишу сценарий, который сохранит каждую строку в переменной памяти и назначит ее выходной строке, когда строка заполнена и присутствует другая строка.
Dim strLine As String = String.Empty Dim strDelimiter As String = ";" Public Sub EmptyMemoryVariables() strLine = String.Empty End Sub Public Sub AssignMemoryVariablesToOutput() With Output0Buffer .AddRow() .NewRow = strLine End With End Sub Public Function AreVariablesEmpty() As Boolean If strLine = "" Then Return True Else Return False End If End Function Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer) Dim strColumns As String() = Row.Column0.Split(CChar(strDelimiter)) If strColumns.Length = 5 Then If Not AreVariablesEmpty() Then AssignMemoryVariablesToOutput() EmptyMemoryVariables() End If strLine = Row.Column0 AssignMemoryVariablesToOutput() EmptyMemoryVariables() Else If strLine.Split(CChar(strDelimiter)).Length = 5 Then AssignMemoryVariablesToOutput() EmptyMemoryVariables() End If strLine &= Row.Column0 End If
Во втором сценарии COmponent я разделит каждую строку на столбцы
Dim strDelimiter As String = ";"
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Dim strColumns As String() = Row.NewRow.Split(CChar(strDelimiter))
Row.ID = strColumns(0)
Row.NAME = strColumns(1)
Row.DOB = strColumns(2)
Row.NOTES = strColumns(3)
Row.CLASSID = strColumns(4)
End Sub
Важное примечание: предоставленный код не оптимален, может потребоваться дополнительная проверка или он может быть проще и лучше, но я пытаюсь дать вам способ решения этой проблемы.
спасибо за все предложения. Оказалось, что производитель изменил кодировку файла с Ascii на unicode. изменение пакета для чтения правильной кодировки помогло.
В вашем компоненте Диспетчера соединений с плоскими файлами у вас есть свойство, имя которого я забыл, в нем вы можете установить разделитель строк ({CR}{LF}
, {LF}
, {CR}
, ... и т. Д.).
Пожалуйста, попробуйте настроить это свойство, я думаю, это сработает.
У меня была аналогичная проблема. У меня был файл CSV с LF в качестве терминатора. Однако у клиента также был CRLF в двух столбцах, и это вызывало ошибку разделителя для столбца не найден.
Мне потребовалось несколько дней поиска решений в Google, проб и ошибок, но все заработало.
В итоге мне понадобились два скриптовых компонента.
В первом компоненте Script у меня был столбец с именем Output0 string с длиной 4000. В сценарии (см. Ниже) я использовал ReadToEnd для загрузки данных, заменил CRLF пустой строкой, а затем разделил на строки с LF как Терминатор.
using System.IO;
using System.Text;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
private StreamReader textReader;
private string collateralFile;
public override void AcquireConnections(object Transaction)
{
IDTSConnectionManager100 connMgr = this.Connections.Collateral;
collateralFile = (string)connMgr.AcquireConnection(null);
}
public override void PreExecute()
{
base.PreExecute();
}
public override void CreateNewOutputRows()
{
StreamReader textReader = new StreamReader(collateralFile);
string collatFile = textReader.ReadToEnd();
collatFile = collatFile.Replace("\r\n", " ");
String[] lines = collatFile.Split(new char[] { '\n' });
textReader.Close();
string nextLine;
for (int i = 0; i < lines.Length; i++)
{
if (lines[i] != null)
{
nextLine = lines[i];
if (!String.IsNullOrEmpty(nextLine))
{
Output0Buffer.AddRow();
Output0Buffer.Output0 = nextLine;
}
}
}
}
}
Я попытался снова разделить его на столбцы, но он вернул нулевые значения, поэтому во втором компоненте скрипта я создал свои столбцы и загрузил в них данные в скрипте.
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
String[] columns = Row.Output0.Split(',');
Row.Description = columns[0];
Row.LegalDescription = columns[1];
Row.Address1ParsedLine1 = columns[2];
Row.Address1ParsedLine2 = columns[4];
Row.Address1ParsedCityname = columns[5];
Row.Address1ParsedStatecode = columns[6];
Row.Address1ParsedPostalcode = columns[7];
}