2014-12-05 173 views
9

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

令我感到困惑的特例是一张有7列的表格。数据文件只有前4列,其余应该保留NULL。

他们通常是这样的:

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

该行终止一直是我的问题的最大来源。

当我尝试使用“\ n”时,批量插入在截断错误上失败 - 它似乎将该文件视为一个长字符串,并且只能正确划分列,直到列用完为止(因此截断错误) 。

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

到目前为止,只有一个用于将数据实际存入表中的工具是“\ l”。有谁知道这意味着什么?我已经搜索了很多,但似乎没有关于它的文档。这或我一直在完全看错了地方。

\ l作为rowterminator的奇怪之处在于,即使它成功加载,它仍然不尊重rowterminator ...这些行只是加载到所有7列中,并在看似随机的间隔中分割。

任何人有什么想法?我应该澄清一些吗?

回答

7

你遇到的问题其实不是由于行终结者。我怀疑,有文件错误的结束一起,你也看到类似以下的东西:

Msg 4864, Level 16, State 1, Line 1
Bulk load data conversion error (type mismatch or invalid character for the specified codepage) for row 1, column 4 ({column_name}).

虽然我说的线以下是关于ROWTERMINATOR仍然有效,真正的问题是由您的发言表示:

[the] table that has 7 columns. The data file only has the first 4 columns, the rest should be left NULL.

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

我想你可以逃脱更容易OPENROWSET(BULK...),这样就可以做到以下几点:

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

但是,这并不让你指定一个ROWTERMINATOR不使用格式文件。因此,无论在哪种情况下都需要格式文件。

OR,你可以只导入到只有4列,然后是一个不同的临时表:

  • 转储到当前STAGING_TABLE,或

  • 做一个ALTER TABLE添加3个缺失列(仅添加3个可NULL字段比将数据从一个表传输到另一个表更为高效:-)。

OR,由@PhilipKelley在这个答案的评论中提到,你可以创建一个只是那些四个字段的景色,有成为目标/目标。如果您正在执行适当的步骤以使操作被最低限度地记录下来,则Prerequisites for Minimal Logging in Bulk Import的MSDN页面不会以某种方式说出使用View的效果。


最有可能的\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个逗号。

+1

谢谢,您提出的替代建议最终能够解决我的问题。这绝对是我在数据文件和登台表之间有不同列的事实。尽管如此,我仍然对此感到困惑,因为还有几个其他进程按照同样的方式构建,并且功能正确,尽管它们的列也不匹配。 在任何情况下,这一切都奏效了。谢谢! – 2014-12-11 16:40:27

+1

@RazzleDazzle很高兴解决了它!关于其他类似的工作,你确定a)他们正在使用'BULK INSERT'而不是'OPENROWSET(BULK ...)'或'BCP.EXE',以及b)如果他们使用' BULK INSERT',他们还没有使用格式文件?我没有看到他们有可能在使用'BULK INSERT'的同时拥有不同数量的列和格式文件,除非这些列是IDENTITY或类似的东西(不能被插入)。 PS,你最终选择哪个实际修补程序?只是好奇:) – 2014-12-11 16:46:48

+0

@RazzleDazzle只是FYI:我只是在目标表中额外的列进行测试。试图将它作为'DATETIME NOT NULL DEFAULT(GETDATE())'和'INT NOT NULL IDENTITY(1,1)',并且由于列数不匹配而失败。所以不知道没有格式化文件或使用“BULK INSERT”之外的其他进程如何工作。 – 2014-12-11 16:57:31

0

我会评论这些,但我的声望还不够高。

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

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

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

+0

'\ l'只是“反斜杠l”。它不是一个解释转义序列。该OP在0x0A == \ n ==换行符中是正确的。但问题是源文件和目标表之间的字段号码不匹配。 – 2014-12-08 18:17:08

0

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

例C在该页面的底部说,使用了Unix行结尾的这个动态SQL:

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模式传输文件吗?或者,您可以通过众多的line ending changers(如unix2dostodos)运行该文件吗?

我知道SSIS允许你为行终止符指定换行符,就像导入/导出向导一样。如果这是一个选项,你可以看看。您必须非常精确地在数据文件中定义列,并且对于列数很多的文件非常繁琐,但是您通常会获得更多选项,例如引用字段标识符等。

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

+0

我绝对同意使用FTP/SFTP/FTPS来指定ASCII模式并让它进行转换。不幸的是,单靠这一点在这里没有帮助,因为问题不在于行终结符:源文件和目标表的字段数目不同,但没有指定格式文件来处理该问题。 – 2014-12-08 18:14:05