从固定格式文本文件批量插入会忽略rowterminator

时间:2012-05-22 19:42:41

标签: sql-server-2008 tsql format bulkinsert

我有很多平面(文本)文件,我想每天导入SQLSERVER表。现在,在我编写程序时,我只想导入一个文件。当然我可以编写c#代码来做到这一点,但我觉得这不是正确的方法,我想使用像xml格式文件的批量插入这样的东西。

我的第一个示例文件看起来像这样(sample.dat):

Q     RR201110010000000002000000000000232000
N     X4201110010000000001500000000000160000

注意:此文件中的hexdump显示每行都以一个换行符完全终止 - 无论是更多还是更少。

我的xml翻译文件如下所示:

<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <RECORD>
  <FIELD ID="1" xsi:type="CharFixed" LENGTH="6"/>
  <FIELD ID="2" xsi:type="CharFixed" LENGTH="2"/>
  <FIELD ID="3" xsi:type="CharFixed" LENGTH="8"/>
  <FIELD ID="4" xsi:type="CharFixed" LENGTH="14"/>
  <FIELD ID="5" xsi:type="CharFixed" LENGTH="14"/>
 </RECORD>
 <ROW>
  <COLUMN SOURCE="1" NAME="c1" xsi:type="SQLNCHAR"/>
  <COLUMN SOURCE="2" NAME="c2" xsi:type="SQLNCHAR"/>
  <COLUMN SOURCE="3" NAME="c3" xsi:type="SQLCHAR"/>
  <COLUMN SOURCE="4" NAME="c4" xsi:type="SQLINT" />
  <COLUMN SOURCE="5" NAME="c5" xsi:type="SQLINT" />
 </ROW>
</BCPFORMAT>

我的查询如下:

SET LANGUAGE us_english;
GO
SET DATEFORMAT ymd;
go
BULK INSERT 
  PROJ.dbo.Costs
  FROM 'C:\somewhere\test01\SAMPLE.DAT'
  WITH
  (
  DATAFILETYPE ='CHAR',
  FORMATFILE='C:\somewhere\test01\TRANSLATE02.XML',
  ERRORFILE='C:\somewhere\test01\ERRORS.TXT',
  ROWTERMINATOR='\n'
  )
  GO

当我运行此脚本时,从第2行开始出现溢出错误。(即,第1行似乎已正确转换,但我没有在sql表中看到它。)ERRORS.TXT的hexdump显示第一个错误行(第2行)以换行符开头!当然,这会导致第4场溢出!所以看起来剧本不了解ROWTERMINATOR。我试过'\ n','\ r','\ r \ n','\ n \ r'以防它没有看到\ r \ n。徒劳无功。

我还尝试了一个稍微不同的sql命令 bulk insert txt error with ROWTERMINATOR

并得到了同样的错误。

对我缺少什么的想法?

根据要求,这是一个sample.dat:

的munged十六进制转储
000000: 41 20 20 20  20 20 XX XX  32 30 31 31  31 30 30 31  Q     RR20111001
000010: 30 30 30 30  30 30 30 30  30 31 35 30  30 30 30 30  0000000001500000
000020: 30 30 30 30  30 30 31 35  30 30 30 30  0A ZZ 20 20  000000150000.N
000030: 20 20 20 XX  XX 32 30 31  31 31 30 30  31 30 30 30     X420111001000
000040: 30 30 30 30  30 30 32 30  30 30 30 30  30 30 30 30  0000002000000000
000050: 30 30 30 32  33 32 30 30  30 0A ZZ 20  20 20 20 20  000232000.Y

请注意,XX和ZZ被屏蔽(不是真实数据)0A是换行符,它是最后一个零(十六进制30)和开始下一行的ZZ字符之间的唯一内容。希望这不会太混乱。

我的解决方案有效,但是,这个问题也在这里讨论,解决方案对我来说似乎更好(虽然我还没有确认,我想我会尝试下一个文件)。 Bulk insert rowterminator issue

4 个答案:

答案 0 :(得分:1)

您必须使用SSIS“SQL Server Integration Services”将数据从文件转换为数据库。 你可以在SQL Server中转换这个转换每天自动转换。

答案 1 :(得分:1)

答案(好吧,至少有一个答案)简直令人尴尬。

我刚刚在XML中的FIELDS列表中添加了一个伪造的,一个字符字段。

<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <RECORD>
  <FIELD ID="1" xsi:type="CharFixed" LENGTH="6"/>
  <FIELD ID="2" xsi:type="CharFixed" LENGTH="2"/>
  <FIELD ID="3" xsi:type="CharFixed" LENGTH="8"/>
  <FIELD ID="4" xsi:type="CharFixed" LENGTH="14"/>
  <FIELD ID="5" xsi:type="CharFixed" LENGTH="14"/>
  <FIELD ID="6" xsi:type="CharFixed" LENGTH="1"/>
 </RECORD>
 <ROW>
  <COLUMN SOURCE="1" NAME="c1" xsi:type="SQLNCHAR"/>
  <COLUMN SOURCE="2" NAME="c2" xsi:type="SQLNCHAR"/>
  <COLUMN SOURCE="3" NAME="c3" xsi:type="SQLCHAR"/>
  <COLUMN SOURCE="4" NAME="c4" xsi:type="SQLINT" />
  <COLUMN SOURCE="5" NAME="c5" xsi:type="SQLINT" />
 </ROW>
</BCPFORMAT>

注意我没有写最后一个字段(带有相应的COLUMN标记)。这将EOL(/ n)读入虚拟字段。如果这不是BULK INSERT命令中ROWTERMINATOR行为的错误,那么它至少非常不直观。也就是说,ROWTERMINATOR似乎是一个NOOP。

观察1:虽然第3列是YYYYMMDD格式的日期,但相应的SOURCE 3实际上是SMALLDATETIME。它会自动正确转换它。

观察2:源3和4定义为十进制(14,2)。我认为这将扩展输入以使用相应字段中的最后2个字符作为百分之一。我可以。找到一种自动缩放的方法(首选)或b。将后处理除以100.(这是另一个问题 - 只是在这里注意,因为它对我来说似乎很有趣。)

无论哪种方式,这似乎是问题的一个解决方案。 感谢您的回复。

附录(旁边): 我决定使用选项b(如观察2中所述),使用sql命令末尾的UPDATE SET命令将money字段除以100。

最终产品将是一个多次调用“sqlcmd”的批处理文件 - 然后在尾端运行一个perl脚本来检查各种错误文件中的条目。

另外一件事:我注意到当我运行它时,BULK INSERT命令中列出的错误文件必须不存在;否则,这本身会产生不同的错误!我会在预处理过程中处理这个问题。

无论如何,再次感谢。

答案 2 :(得分:1)

SQL Server article on XML schema files for fixed-format text具有在RECORD元素中指定的终止符:

<RECORD>
  <FIELD ID="1" xsi:type="CharFixed" LENGTH="10"/>
  <FIELD ID="2" xsi:type="CharFixed" LENGTH="6"/>
  <FIELD ID="3" xsi:type="CharTerm" TERMINATOR="\r\n"
</RECORD>

(请注意上面的拼写错误。)也许这就是为什么忽略查询中的规范的原因。

答案 3 :(得分:0)

尝试char(13),这是SQL回车。还char(13) + char(10),回车/换行。