为什么SSIS在导入UTF-8平面文件时不识别换行{LF}行分隔符?

K R*_*ard 29 sql-server ssis flat-file utf-8 sql-server-2008

我试图使用SSIS将数据从utf-8编码的平面文件导入SQL Server 2008.这就是Notepad ++中行数据的结尾:

在此输入图像描述

我有几个图像显示文件连接管理器的样子:

在此输入图像描述

在此输入图像描述

您可以在文件连接管理器预览中看到数据正确显示.当我尝试导入此数据时,不导入任何行.我收到一条错误消息,指出未找到行分隔符.您可以在文件连接管理器图像中看到标题行分隔符和行分隔符都设置为{LF}.这足以生成正确的预览,所以我迷失了为什么它无法导入.我尝试了许多结果为零的事情:

  • 在SSMS中使用向导导入尝试...相同的结果
  • 尝试使用数据转换,没有影响
  • 尝试将行分隔符设置为(0a),结果相同

[Flat File Source [582]]警告:读取标题行时已到达数据文件的末尾.确保标题行分隔符和要跳过的标题行数是正确的.

感谢您的关注,我非常感谢您提供的任何帮助.

小智 62

原因:

SSIS无法读取该文件,并显示以下警告由于列分隔符Ç("C"与变音符号)和not由于行定界符{LF}(换行).

[Read flat file [1]] Warning: The end of the data file was reached while 
reading header rows. Make sure the header row delimiter and the number of 
header rows to skip are correct.
Run Code Online (Sandbox Code Playgroud)

这是一个示例SSIS包,显示如何使用解决问题Script Component,最后还有另一个模拟您的问题的示例.

解析度:

下面的示例包是写的SSIS 2008 R2.它读取一个带有行分隔符的平面文件{LF}作为单个列值; 然后使用分割数据Script Component将信息插入SQL Server 2008 R2数据库中的表.

使用Notepad ++创建一个包含少量行的简单平面文件.下面的示例文件包含每行的Product IdList Price信息,Ç以列分隔符分隔,每行以{LF}分隔符结尾.

平面文件源

在Notepad ++上,单击Encoding,然后单击Encoding in UTF-8以保存平面文件的UTF-8编码.

编码UTF-8

该示例将使用SQL Server 2008 R2名为的数据库Sora.创建一个dbo.ProductListPrice使用以下给定脚本命名的新表.SSIS会将平面文件数据插入此表.

USE Sora;
GO

CREATE TABLE dbo.ProductListPrice
(
        ProductId   nvarchar(30)    NOT NULL
    ,   ListPrice   numeric(12,2)   NOT NULL
);
GO
Run Code Online (Sandbox Code Playgroud)

使用Business Intelligence Development Studio(BIDS)2008 R2创建SSIS包.将包命名为SO_6268205.dtsx.创建一个名为数据源Sora.ds连接到数据库Sora的SQL Server 2008 R2.

右键单击包内的任何位置,然后单击Variables以查看变量窗格.在包范围中创建一个名为ColumnDelimiterdata type 的新变量,并使用该值设置变量StringSO_6268205Ç

包变量

右键单击Connection Managers并单击New Flat File Connection...以创建连接以读取平面文件.

平面文件连接

在" 平面文件连接管理器编辑器"General页面上,执行以下操作:

  • 连接管理器名称设置为ProductListPrice
  • 描述设置为Flat file connection manager to read product list price information.
  • 选择平面文件路径.我在路径中有文件C:\Siva\StackOverflow\Files\6268205\ProductListPrice.txt
  • {LF}标题行分隔符中选择
  • 校验 Column names in the first data row
  • 点击Columns页面

平面文件连接管理器编辑器 - 常规

在" 平面文件连接管理器编辑器"Columns页面上,验证该文件是否Column delimiter为空并已禁用.点击Advanced页面.

平面文件连接管理器编辑器 - 列

在" 平面文件连接管理器编辑器"Advanced页面上,执行以下操作.

  • 名称设置为LineData
  • 验证列分隔符是否设置为{LF}
  • DataType设置为Unicode string [DT_WSTR]
  • OutputColumnWidth设置为255
  • 单击该Preview页面.

平面文件连接管理器编辑器 - 高级

在" 平面文件连接管理器编辑器 " Preview页面上,验证显示的数据是否正确并单击.OK

平面文件连接管理器编辑器 - 预览

您将在包底部的选项卡上看到数据源Sora和平面文件连接管理器ProductListPriceConnection Managers.

连接经理

拖放Data Flow Task到包的" 控制流"选项卡上并将其命名为File to database - Without Cedilla delimiter

数据流任务1

双击" 数据流任务"以将视图切换到Data Flow程序包上的选项卡.Flat File Source在" 数据流"选项卡上拖放a .双击平面文件源以打开Flat File Source Editor.

在" 平面文件源编辑器"Connection Manager页面上,选择" 平面文件连接管理器",然后单击" 列"页面. ProductListPrice

平面文件源编辑器 - 连接管理器

在" 平面文件源编辑器"Columns页面上,选中该列并单击.LineDataOK

平面文件源编辑器 - 列

将a拖放Script ComponentFlat File Source下面的Data Flow选项卡上,选择并单击.将绿色箭头从平面文件源连接脚本组件.双击脚本组件以打开.TransformationOKScript Transformation Editor

单击" 脚本转换编辑器"上的" 输入列"并选择LineData列.单击" 输入和输出"页面.

脚本转换编辑器 - 输入列

在" 脚本转换编辑器"Inputs and Outputs页面上,执行以下操作.

  • 将输入名称更改为FlatFileInput
  • 将输出名称更改为 SplitDataOutput
  • 选择Output Columns并单击Add Column.再次重复此操作以添加另一列.
  • 命名第一列 ProductId
  • ProductId列的DataType设置为Unicode string [DT_WSTR]
  • 长度设置为30

脚本转换编辑器 - 输入和输出 -  ProductId

在" 脚本转换编辑器"Inputs and Outputs页面上,执行以下操作.

  • 将第二列命名为 ListPrice
  • 将列ListPriceDataType设置为numeric [DT_NUMERIC]
  • 精度设置为12
  • 比例设置为2
  • 单击" 脚本"页面以修改脚本

脚本转换编辑器 - 输入和输出 -  ListPrice

在" 脚本转换编辑器"Script页面上,执行以下操作.

  • 单击ReadOnlyVariables的省略号按钮,然后选择变量User::ColumnDelimiter
  • 点击 Edit Script...

脚本转换编辑器 - 脚本

在脚本编辑器中粘贴以下C#.该脚本执行以下任务.

  • 使用Ç变量User :: ColumnDelimiter中定义的列分隔符值,该方法FlatFileInput_ProcessInputRow将传入的值拆分并将其分配给脚本组件转换中定义的两个输出列.

C#中的脚本组件代码

using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
    public override void PreExecute()
    {
        base.PreExecute();
    }

    public override void PostExecute()
    {
        base.PostExecute();
    }

    public override void FlatFileInput_ProcessInputRow(FlatFileInputBuffer Row)
    {
        const int COL_PRODUCT = 0;
        const int COL_PRICE = 1;

        char delimiter = Convert.ToChar(this.Variables.ColumnDelimiter);
        string[] lineData = Row.LineData.ToString().Split(delimiter);

        Row.ProductId = String.IsNullOrEmpty(lineData[COL_PRODUCT]) 
                            ? String.Empty 
                            : lineData[COL_PRODUCT];

        Row.ListPrice = String.IsNullOrEmpty(lineData[COL_PRICE]) 
                            ? 0 
                            : Convert.ToDecimal(lineData[COL_PRICE]);
    }
}
Run Code Online (Sandbox Code Playgroud)

脚本组件代码 -  C#

拖放OLE DB Destination到" 数据流"选项卡上.将绿色箭头从脚本组件连接OLE DB目标.双击OLE DB目标以打开OLE DB Destination Editor.

OLE DB目标编辑器Connection Manager页面上,执行以下操作.

  • SoraOLE DB连接管理器中选择
  • Table or view - fast load数据访问模式中选择
  • [dbo].[ProductListPrice]表格或视图的名称中选择
  • 单击" 映射"页面

OLE DB目标编辑器 - 连接管理器

如果输入和输出列名称相同Mappings,OLE DB目标编辑器上的单击页面将自动映射列.点击OK.

OLE DB目标编辑器 - 映射

配置完所有组件后," 数据流"选项卡应如下所示.

数据流选项卡

select * from dbo.ProductListPriceSQL Server Management Studio(SSMS)中执行查询以查找表中的行数.在执行包之前它应该是空的.

包执行前的表中的行

执行包.您会注意到该包已成功处理9行.平面文件包含10行,但第一行是带有列名称的标题.

包没有分隔符的包执行

select * from dbo.ProductListPriceSQL Server Management Studio(SSMS)中执行查询以查找成功插入表中的9行.数据应与平面文件数据匹配.

包执行后表中的行

上面的示例说明了如何使用脚本组件手动拆分数据,因为平面文件连接管理器在配置列分隔符时遇到错误Ç

问题模拟:

此示例显示了使用列分隔符配置的单独的平面文件连接管理器Ç,该分隔符执行但遇到警告并且不处理任何行.

右键单击Connection Managers并单击New Flat File Connection...以创建连接以读取平面文件.在" 平面文件连接管理器编辑器"General页面上,执行以下操作:

  • 连接管理器名称设置为ProductListPrice_Cedilla
  • 将描述设置为 Flat file connection manager with Cedilla column delimiter.
  • 我在路径中有文件C:\Siva\StackOverflow\Files\6268205\ProductListPrice.txt选择平面文件路径.
  • {LF}标题行分隔符中选择
  • 校验 Column names in the first data row
  • 点击Columns页面

平面文件连接管理器编辑器 - 使用Cedilla  -  General

在" 平面文件连接管理器编辑器"Columns页面上,执行以下操作:

  • 行分隔符设置为{LF}
  • 可以禁用列分隔符字段.点击Reset Columns
  • 列分隔符设置为Ç
  • 点击Advanced页面

平面文件连接管理器编辑器 - 使用Cedilla  - 列

在" 平面文件连接管理器编辑器"Advanced页面上,执行以下操作:

  • 名称设置为ProductId
  • ColumnDelimiter设置为Ç
  • DataType设置为Unicode string [DT_WSTR]
  • 长度设置为30
  • 单击列 ListPrice

平面文件连接管理器编辑器 - 使用Cedilla  - 高级 -  ProductId

在" 平面文件连接管理器编辑器"Advanced页面上,执行以下操作:

  • 名称设置为ListPrice
  • ColumnDelimiter设置为{LF}
  • DataType设置为numeric [DT_NUMERIC]
  • DataPrecision设置为12
  • DataScale设置为2
  • 点击 OK

平面文件连接管理器编辑器 - 使用Cedilla  - 高级 -  ListPrice

将a拖放Data Flow task到" 控制流"选项卡上并将其命名为File to database - With Cedilla delimiter.禁用第一个数据流任务.

数据流任务2

使用Flat File Source和配置第二个数据流任务OLE DB Destination

数据流选项卡 -  2

双击平面文件源以打开Flat File Source Editor.在Flat File Source EditorConnection Manager页面上,选择Flat File Connection Manager,然后单击Columns页面以配置列.点击. ProductListPrice_CedillaOK

平面文件源编辑器 -  Cedilla

执行包.所有组件都将显示绿色,表示该过程成功但不会处理任何行.您可以看到Flat File Source和之间没有行号指示OLE DB Destination

包执行 -  Cedilla

单击Progress选项卡,您将看到以下警告消息.

[Read flat file [1]] Warning: The end of the data file was reached while 
reading header rows. Make sure the header row delimiter and the number of 
header rows to skip are correct.
Run Code Online (Sandbox Code Playgroud)

进度警告消息

  • 这是一个令人难以置信的答案.我不能表达我的感谢.这应该是stackoverflow的一个突出示例. (7认同)
  • 我刚刚离开,但让我明白:惊人的答案! (3认同)