【问题标题】:Bulk Insert - Row Terminator for UNIX file + "\l" row terminator批量插入 - UNIX 文件的行终止符 + "\l" 行终止符
【发布时间】:2015-02-04 02:06:24
【问题描述】:

所以一段时间以来,我一直在努力解决 BULK INSERT 的一个令人困惑的问题。这些文件来自Linux机器,当我在十六进制编辑模式/记事本++中查看它们时,它们似乎只有一个换行符(0A)作为行终止符。我将批量插入语句存储在一个表中,稍后作业会从中选择并执行表中的语句以将数据加载到临时表中。

让我感到困惑的特殊情况是一个有 7 列的表。数据文件只有前 4 列,其余应为 NULL。

通常它们看起来像这样:

BULK INSERT STAGING_TABLE  FROM 'FILE_LOCATION'  
WITH     (   
DATAFILETYPE = 'widechar'
,   FIELDTERMINATOR = ','
,   ROWTERMINATOR = 'something_here'   
);

行终止符是我问题的最大根源。

当我尝试使用“\n”时,批量插入因截断错误而失败——它似乎将文件视为一个长字符串,并且仅正确分隔列,直到它用完列(因此出现截断错误)。

当我使用“0x0a”时,批量插入因“意外的文件结尾”错误而失败。文件末尾有一个空行,但即使我删除它仍然会抛出相同的错误,所以我不确定那里有什么问题。

到目前为止,唯一一个可以将数据实际放入表中的方法是“\l”。有谁知道那是什么意思?我进行了广泛的搜索,但似乎没有关于它的文档。那或者我一直在寻找错误的地方。

将 \l 作为行终止符的奇怪之处在于,即使它成功加载,它仍然不尊重行终止符...行只是被加载到所有 7 列中,并以看似随机的间隔拆分。

有人知道吗?我应该再澄清一些吗?

【问题讨论】:

    标签: sql sql-server-2008-r2 bulkinsert


    【解决方案1】:

    您遇到的问题实际上不是由于行终止符。我怀疑,除了文件结束错误,您还看到了类似以下内容:

    消息 4864,第 16 级,状态 1,第 1 行
    第 1 行第 4 列 ({column_name}) 的批量加载数据转换错误(指定代码页的类型不匹配或无效字符)。

    虽然我在下面所说的关于ROWTERMINATOR 的内容仍然有效,但您的陈述表明了真正的问题:

    [the] 有 7 列的表。数据文件只有前 4 列,其余为 NULL。

    这就是问题所在。使用BULK INSERT 时,数据文件的字段数必须与插入的表的字段数相同。如果不是这种情况,那么您必须使用FORMATFILE ='format_file_path' 选项,在这种情况下您需要创建Format File 并指定位置。

    我认为您可以使用更简单的OPENROWSET(BULK...),以便您可以执行以下操作:

    INSERT INTO STAGING_TABLE
       SELECT *
       FROM   OPENROWSET(BULK 'FILE_LOCATION' ...);
    

    但这不允许您在不使用格式文件的情况下指定ROWTERMINATOR。因此,无论哪种情况,您都需要格式文件。

    或者,您可以只导入到只有 4 列的不同临时表中,然后:

    • 将其转储到您当前的 STAGING_TABLE 中,或者

    • 执行ALTER TABLE 以添加 3 个缺失的列(仅添加 3 个 NULLable 字段比将数据从一个表传输到另一个更有效:-)。

    OR,正如@PhilipKelley 在对此答案的评论中提到的那样,您可以创建一个仅包含这四个字段的视图并将其作为目标/目标。而且,如果您正在执行适当的步骤以使操作被最小化记录,Prerequisites for Minimal Logging in Bulk Import 的 MSDN 页面不会以一种方式或另一种方式说明如果您使用视图会产生什么效果。


    \l 很可能只是被解释为这两个文字字符,因此当您尝试它时它不尊重 rowterminator

    0x0A 将按照我的测试工作,它的行为符合预期。您的声明应如下所示:

    BULK INSERT STAGING_TABLE
    FROM 'FILE_LOCATION'  
    WITH (   
           DATAFILETYPE = 'widechar',
           FIELDTERMINATOR = ',',
           ROWTERMINATOR = '0x0A'
    );
    

    我在最后一行的末尾尝试了使用和不使用0x0A 字符,两者的效果都一样。

    然后我从其中一行中删除了一个逗号,留下的字段少于完整的字段集,这就是我收到以下错误的时候:

    Msg 4832, Level 16, State 1, Line 2
       Bulk load: An unexpected end of file was encountered in the data file.
    Msg 7399, Level 16, State 1, Line 2
       The OLE DB provider "BULK" for linked server "(null)" reported an error. The 
                     provider did not give any information about the error.
    Msg 7330, Level 16, State 2, Line 2
       Cannot fetch a row from OLE DB provider "BULK" for linked server "(null)".
    

    确保数据文件中的所有行都具有所需数量的字段分隔符(在本例中为,)。您提到文件中有 4 列,因此每行应该是 3 个逗号。

    【讨论】:

    • 谢谢,您提出的替代建议最终能够解决我的问题。这绝对是我在数据文件和临时表之间有不同的列的事实。我对此感到困惑,因为尽管列不匹配,但还有其他几个以相同方式构造的进程正常运行。无论如何,这一切都解决了。谢谢!
    • @RazzleDazzle 很高兴它成功了!关于其他正在运行的类似进程,您确定 a) 他们使用的是 BULK INSERT,而不是 OPENROWSET(BULK...)BCP.EXE,以及 b) 如果他们使用的是 BULK INSERT,那么他们也没有使用格式文件?我看不出他们怎么可能使用BULK INSERT,同时拥有不同数量的列并且没有格式文件,除非这些列是 IDENTITY 或类似的东西(无法插入)。 PS,您最终使用了哪个实际修复程序?只是好奇:)
    • @RazzleDazzle 仅供参考:我刚刚再次测试了目标表中的一个额外列。将其作为DATETIME NOT NULL DEFAULT(GETDATE())INT NOT NULL IDENTITY(1, 1) 进行了尝试,但都由于列数不匹配而失败。所以不确定这些其他进程如何在没有格式文件或使用BULK INSERT以外的其他东西的情况下工作。
    • 我最终使用了一个“暂存”暂存表,其中包含正确的列,并带有额外的列。
    • 不匹配的批量插入列的另一个选项:在目标表上创建一个与正在加载的数据匹配的视图。在这里,视图将有四列,您将批量插入视图,“额外”三列将获取默认值(NULL,或在它们上定义默认约束)。如果文件和表格列的顺序不同(甚至是随机的),这将非常有用。
    【解决方案2】:

    我想问这些问题,但我的声誉还不够高。

    我相信“\l”是“换行符”,所以这会让您在文件编码中看到 0A。

    我的第一个问题是,您的数据文件采用什么字符编码?你的表列的数据类型是什么?

    我猜这将是一个字符编码问题。我看到你的 DATAFILETYPE 是 'widechar' 你确认你的源文件是 Unicode 吗?而当你插入数据并选择它回来时,它看起来是否好像保留了字符编码?

    【讨论】:

    • \l 只是“反斜杠 l”。它不是一个解释的转义序列。 OP 在 0x0A == \n == 换行中是正确的。但问题是源文件和目标表之间的字段编号不匹配。
    【解决方案3】:

    This 似乎表明使用换行符作为行终止符'\n' 将自动转换为'\r\n'。它说这仅适用于 bcp,但显然还有其他事情发生。

    该页面底部的示例 C 表示将此动态 SQL 用于 Unix 行尾:

    DECLARE @bulk_cmd varchar(1000);
    SET @bulk_cmd = 'BULK INSERT AdventureWorks2012.Sales.SalesOrderDetail
    FROM ''<drive>:\<path>\<filename>'' 
    WITH (ROWTERMINATOR = '''+CHAR(10)+''')';
    EXEC(@bulk_cmd);
    

    这使它看起来像是一个已知问题。

    如果您从 FTP/SFTP 站点检索文件,您可以以 ASCII 模式传输文件吗?或者,您可以通过unix2dostodos 等众多line ending changers 中的任何一个运行该文件吗?

    我知道 SSIS 允许您只为行终止符指定换行符,导入/导出向导也是如此。如果这是一个选项,你可以看看那个。您必须非常精确地定义数据文件中的列,对于具有大量列的文件来说非常繁琐,但您通常可以获得更多选项,例如带引号的字段标识符等。

    而且我不知道\l 代表什么控制字符。它似乎没有记录在任何地方。

    【讨论】:

    • 我绝对同意使用 FTP/SFTP/FTPS 来指定 ASCII 模式并让它进行转换。不幸的是,仅此一项无济于事,因为问题不在于行终止符:源文件和目标表之间的字段数不同,但没有指定格式文件来处理它。
    猜你喜欢
    • 2023-03-08
    • 1970-01-01
    • 2017-07-18
    • 2016-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多