Синтаксический анализ текстовых файлов нестандартного формата в компоненте сценария
Если исходные данные организованы в нестандартном формате, может оказаться удобнее объединить всю логику синтаксического анализа в единый скрипт вместо создания цепочки преобразований служб Integration Services, приводящих к тому же результату.
Пример 1. Синтаксический анализ записей, разделенных на строки
Пример 2. Разделение родительских и дочерних записей
Примечание |
---|
Если нужно создать компонент, который будет удобно использовать в нескольких задачах потока данных и нескольких пакетах, рекомендуется начать разработку пользовательского компонента потока данных с этого образца компонента сценария. Дополнительные сведения см. в разделе Разработка пользовательского компонента потока данных. |
Пример 1. Синтаксический анализ записей, разделенных на строки
В этом примере берется текстовый файл, в котором каждый столбец данных размещается на новой строке, и с помощью синтаксического анализа, реализованного в компоненте сценария, преобразуется в целевую таблицу.
Дополнительные сведения о настройке компонента сценария для использования в качестве преобразования в потоке данных см. в разделах Создание синхронного преобразования с помощью компонента скрипта и Создание асинхронного преобразования с помощью компонента скрипта.
Настройка этого образца компонента скрипта
Создайте и сохраните под именем rowdelimiteddata.txt текстовый файл, содержащий следующие исходные данные:
FirstName: Nancy LastName: Davolio Title: Sales Representative City: Seattle StateProvince: WA FirstName: Andrew LastName: Fuller Title: Vice President, Sales City: Tacoma StateProvince: WA FirstName: Steven LastName: Buchanan Title: Sales Manager City: London StateProvince:
Откройте среду Management Studio и установите соединение с экземпляром SQL Server.
Выберите целевую базу данных и откройте окно нового запроса. Чтобы создать целевую таблицу, выполните в окне запросов следующий скрипт.
create table RowDelimitedData ( FirstName varchar(32), LastName varchar(32), Title varchar(32), City varchar(32), StateProvince varchar(32) )
В среде BI Development Studio создайте новый пакет служб Integration Services с именем ParseRowDelim.dtsx.
Добавьте в пакет диспетчер соединений с неструктурированными файлами, назовите этот диспетчер «RowDelimitedData» и настройте на соединение с файлом rowdelimiteddata.txt, созданным на предыдущем шаге.
Добавьте в пакет диспетчер соединений OLE DB и настройте его на соединение с экземпляром SQL Server и базой данных, в которой создана целевая таблица.
Добавьте в пакет задачу потока данных и щелкните вкладку Поток данных конструктора служб SSIS.
Добавьте к потоку данных источник «Неструктурированный файл» и настройте его на использование диспетчера соединений RowDelimitedData. На странице Столбцы в редакторе источника «Неструктурированный файл» выберите единственный доступный внешний столбец.
Добавьте в поток данных компонент сценария и настройте его в качестве преобразования. Соедините выход источника «Неструктурированный файл» с компонентом сценария.
Дважды щелкните компонент сценария, чтобы открыть редактор преобразования «Сценарий».
На странице Входные столбцы в редакторе преобразования «Сценарий» выберите единственный доступный входной столбец.
На странице Входы и выводыредактора преобразования «Скрипт» выберите вывод 0 и установите его SynchronousInputID в значение «Нет». Создайте 5 выходных столбцов со строковым типом [DT_STR] и длиной 32:
FirstName
LastName
Title
City
StateProvince
На странице Сценарийредактора преобразования «Сценарий» нажмите кнопку Изменить сценарий и введите код, содержащийся в классе ScriptMain примера. Закройте среду разработки сценариев и редактор преобразования «Сценарий».
Добавьте в поток данных назначение «SQL Server». Настройте его на использование диспетчера соединений OLE DB и таблицы RowDelimitedData. Настройте выход компонента сценария на это назначение.
Запустите пакет. По завершении работы пакета исследуйте записи в целевой таблице SQL Server.
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Dim columnName As String
Dim columnValue As String
' Check for an empty row.
If Row.Column0.Trim.Length > 0 Then
columnName = Row.Column0.Substring(0, Row.Column0.IndexOf(":"))
' Check for an empty value after the colon.
If Row.Column0.Substring(Row.Column0.IndexOf(":")).TrimEnd.Length > 1 Then
' Extract the column value from after the colon and space.
columnValue = Row.Column0.Substring(Row.Column0.IndexOf(":") + 2)
Select Case columnName
Case "FirstName"
' The FirstName value indicates a new record.
Me.Output0Buffer.AddRow()
Me.Output0Buffer.FirstName = columnValue
Case "LastName"
Me.Output0Buffer.LastName = columnValue
Case "Title"
Me.Output0Buffer.Title = columnValue
Case "City"
Me.Output0Buffer.City = columnValue
Case "StateProvince"
Me.Output0Buffer.StateProvince = columnValue
End Select
End If
End If
End Sub
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
string columnName;
string columnValue;
// Check for an empty row.
if (Row.Column0.Trim().Length > 0)
{
columnName = Row.Column0.Substring(0, Row.Column0.IndexOf(":"));
// Check for an empty value after the colon.
if (Row.Column0.Substring(Row.Column0.IndexOf(":")).TrimEnd().Length > 1)
// Extract the column value from after the colon and space.
{
columnValue = Row.Column0.Substring(Row.Column0.IndexOf(":") + 2);
switch (columnName)
{
case "FirstName":
// The FirstName value indicates a new record.
this.Output0Buffer.AddRow();
this.Output0Buffer.FirstName = columnValue;
break;
case "LastName":
this.Output0Buffer.LastName = columnValue;
break;
case "Title":
this.Output0Buffer.Title = columnValue;
break;
case "City":
this.Output0Buffer.City = columnValue;
break;
case "StateProvince":
this.Output0Buffer.StateProvince = columnValue;
break;
}
}
}
}
Пример 2. Разделение родительских и дочерних записей
В этом примере берется текстовый файл, в котором за строкой-разделителем следует родительская строка, а за ней — неопределенное количество дочерних строк, и с помощью синтаксического анализа, реализованного в компоненте сценария, преобразуется в две правильным образом нормализованные целевые таблицы — родительскую и дочернюю. Этот простой пример легко адаптировать для исходных файлов, использующих несколько строк или столбцов для каждой родительской и дочерней записи: главное, чтобы начало и конец каждой записи были как-то обозначены.
Внимание! |
---|
Данный образец предназначен только для демонстрационных целей. Если запустить образец несколько раз, он вставит в целевую таблицу повторяющиеся ключевые значения. |
Дополнительные сведения о настройке компонента сценария для использования в качестве преобразования в потоке данных см. в разделах Создание синхронного преобразования с помощью компонента скрипта и Создание асинхронного преобразования с помощью компонента скрипта.
Настройка этого образца компонента скрипта
- Создайте и сохраните под именем parentchilddata.txt текстовый файл, содержащий следующие исходные данные:
******** PARENT 1 DATA child 1 data child 2 data child 3 data child 4 data ******** PARENT 2 DATA child 5 data child 6 data child 7 data child 8 data ********
Откройте среду Среда SQL Server Management Studio и установите соединение с экземпляром SQL Server.
Выберите целевую базу данных и откройте окно нового запроса. Чтобы создать целевые таблицы, выполните в окне запросов следующий сценарий.
CREATE TABLE [dbo].[Parents]( [ParentID] [int] NOT NULL, [ParentRecord] [varchar](32) NOT NULL, CONSTRAINT [PK_Parents] PRIMARY KEY CLUSTERED ([ParentID] ASC) ) GO CREATE TABLE [dbo].[Children]( [ChildID] [int] NOT NULL, [ParentID] [int] NOT NULL, [ChildRecord] [varchar](32) NOT NULL, CONSTRAINT [PK_Children] PRIMARY KEY CLUSTERED ([ChildID] ASC) ) GO ALTER TABLE [dbo].[Children] ADD CONSTRAINT [FK_Children_Parents] FOREIGN KEY([ParentID]) REFERENCES [dbo].[Parents] ([ParentID])
В среде Business Intelligence Development Studio создайте новый пакет служб Integration Services с именем «SplitParentChild.dtsx».
Добавьте в пакет диспетчер соединений с неструктурированными файлами, назовите этот диспетчер «ParentChildData» и настройте на соединение с файлом parentchilddata.txt, созданным на предыдущем шаге.
Добавьте в пакет диспетчер соединений OLE DB и настройте его на соединение с экземпляром SQL Server и базой данных, в которой созданы целевые таблицы.
Добавьте в пакет задачу потока данных и щелкните вкладку Поток данных конструктора служб SSIS.
Добавьте к потоку данных источник «Неструктурированный файл» и настройте его на использование диспетчера соединений ParentChildData. На странице Столбцы в редакторе источника «Неструктурированный файл» выберите единственный доступный внешний столбец.
Добавьте в поток данных компонент сценария и настройте его в качестве преобразования. Соедините выход источника «Неструктурированный файл» с компонентом сценария.
Дважды щелкните компонент сценария, чтобы открыть редактор преобразования «Сценарий».
На странице Входные столбцы в редакторе преобразования «Сценарий» выберите единственный доступный входной столбец.
На странице Входы и выводыредактора преобразования «Сценарий», установите выводу значение 0, переименуйте его в ParentRecords и задайте для параметра SynchronousInputID значение «Нет». Создайте 2 выходных столбца.
ParentID (первичный ключ), имеющий тип четырехбайтового целого числа со знаком [DT_I4].
ParentRecord, строкового типа [DT_STR], длиной 32 символа.
Создайте второй вывод и установите для него имя ChildRecords. Параметр SynchronousInputID для нового вывода уже установлен в значение «Нет». Создайте 3 выходных столбца.
ChildID (первичный ключ), имеющий тип четырехбайтового целого числа со знаком [DT_I4].
ParentID (внешний ключ), также типа «четырехбайтовое целое число со знаком» [DT_I4].
ChildRecord, строкового типа [DT_STR], длиной 50 символов.
На странице Сценарийредактора преобразования «Сценарий» нажмите кнопку Изменить сценарий. Вставьте код, приведенный в примере, в класс ScriptMain. Закройте среду разработки сценариев и редактор преобразования «Сценарий».
Добавьте в поток данных назначение «SQL Server». Подключите выход ParentRecords компонента сценария к этому назначению. Настройте его на использование диспетчера соединений OLE DB и таблицы Parents.
Добавьте к потоку данных еще одно назначение «SQL Server». Настройте выход ChildRecords компонента сценария на это назначение. Настройте его на использование диспетчера соединений OLE DB и таблицы Children.
Запустите пакет. По завершении работы пакета исследуйте записи в двух целевых таблицах SQL Server.
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Static nextRowIsParent As Boolean = False
Static parentCounter As Integer = 0
Static childCounter As Integer = 0
' If current row starts with separator characters,
' then following row contains new parent record.
If Row.Column0.StartsWith("***") Then
nextRowIsParent = True
Else
If nextRowIsParent Then
' Current row contains parent record.
parentCounter += 1
Me.ParentRecordsBuffer.AddRow()
Me.ParentRecordsBuffer.ParentID = parentCounter
Me.ParentRecordsBuffer.ParentRecord = Row.Column0
nextRowIsParent = False
Else
' Current row contains child record.
childCounter += 1
Me.ChildRecordsBuffer.AddRow()
Me.ChildRecordsBuffer.ChildID = childCounter
Me.ChildRecordsBuffer.ParentID = parentCounter
Me.ChildRecordsBuffer.ChildRecord = Row.Column0
End If
End If
End Sub
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
int static_Input0_ProcessInputRow_childCounter = 0;
int static_Input0_ProcessInputRow_parentCounter = 0;
bool static_Input0_ProcessInputRow_nextRowIsParent = false;
// If current row starts with separator characters,
// then following row contains new parent record.
if (Row.Column0.StartsWith("***"))
{
static_Input0_ProcessInputRow_nextRowIsParent = true;
}
else
{
if (static_Input0_ProcessInputRow_nextRowIsParent)
{
// Current row contains parent record.
static_Input0_ProcessInputRow_parentCounter += 1;
this.ParentRecordsBuffer.AddRow();
this.ParentRecordsBuffer.ParentID = static_Input0_ProcessInputRow_parentCounter;
this.ParentRecordsBuffer.ParentRecord = Row.Column0;
static_Input0_ProcessInputRow_nextRowIsParent = false;
}
else
{
// Current row contains child record.
static_Input0_ProcessInputRow_childCounter += 1;
this.ChildRecordsBuffer.AddRow();
this.ChildRecordsBuffer.ChildID = static_Input0_ProcessInputRow_childCounter;
this.ChildRecordsBuffer.ParentID = static_Input0_ProcessInputRow_parentCounter;
this.ChildRecordsBuffer.ChildRecord = Row.Column0;
}
}
}
|