从固定格式的文本文件批量插入忽略行终止符

Posted

技术标签:

【中文标题】从固定格式的文本文件批量插入忽略行终止符【英文标题】:Bulk Insert from fixed format text file ignores rowterminator 【发布时间】:2012-05-22 19:42:41 【问题描述】:

我有很多平面(文本)文件,我希望每天将它们导入到 SQLSERVER 表中。现在,当我制定程序时,我只想导入一个文件。当然,我可以编写 c# 代码来执行此操作,但我觉得这不是正确的方法,我想使用诸如批量插入 xml 格式文件之类的东西。

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

Q     RR201110010000000002000000000000232000
N     X4201110010000000001500000000000160000

注意:此文件上的十六进制转储显示每一行都以换行符结束 - 不多也不少。

我的 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 的十六进制转储显示第一个错误行(第 2 行)以换行符开头!当然,这会导致第 4 个字段溢出!所以看起来脚本不理解 ROWTERMINATOR。我尝试了 '\n', '\r', '\r\n', '\n\r' 以防万一它没有看到 \r。徒劳无功。

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

得到了同样的错误。

对我缺少什么有什么想法吗?

根据要求,这是一个经过修改的 sample.dat 十六进制转储:

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

【问题讨论】:

你能显示一个显示换行符的十六进制转储的 sn-p 吗? 文件不是unicode。 我添加了一个掩蔽了几个字符的 munged 十六进制转储。 【参考方案1】:

您必须使用 SSIS“SQL Server 集成服务”将数据从文件转换到数据库。 并且您可以在 SQL Server 中执行此转换,以便每天自动转换。

【讨论】:

感谢您的指点。我没有权限运行或保存我用这个创建的包,但我会问管理员他是否可以设置我。【参考方案2】:

答案(嗯,至少一个答案)非常简单。

我刚刚在 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 命令将货币字段除以 100。

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

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

无论如何,再次感谢。

【讨论】:

注意:我打算在时间用完时接受这个作为答案,除非其他人可以指出涉及 ROWTERMINATOR 工作的解决方案。 遇到了完全相同的问题。感谢您的简单解决方案,因为没有想到它而踢自己。您链接到的替代解决方案(使用 ROWTERMINATOR='0x0A')在使用格式文件时不起作用(使用格式文件时 ROWTERMINATOR 似乎被忽略)【参考方案3】:

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>

(请注意上面的错字。)也许这就是您在查询中的规范被忽略的原因。

【讨论】:

【参考方案4】:

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

【讨论】:

以上是关于从固定格式的文本文件批量插入忽略行终止符的主要内容,如果未能解决你的问题,请参考以下文章

从数据框批量插入到数据库,忽略 Pyspark 中的失败行

批量从Dataframe插入到DB,忽略Pyspark中的失败行

将多套图片批量分别插入对应的word中

怎么把UTF-8编码的文本批量改成ANSI啊!!!!!!!!!?

使用 jdbc 将行批量插入 Spanner 时加载性能低

使用回车行终止符读取/写入固定长度的文本记录