【问题标题】:Problem with SQL bulk insert tab delimited fileSQL批量插入制表符分隔文件的问题
【发布时间】:2021-03-31 14:09:27
【问题描述】:

我在使用批量插入时遇到问题。问题是我正在处理的源文件(制表符分隔)包含以cr/lf 结尾的行,而没有用制表符填充其余行的空列的值。因此,当数据被拉入 SQL Server 时,它会将那些缩短的行合并到前一行中。所以基本上它是将多行组合成一个,而不是将它写成两个单独的行,第一行末尾有空值。

说明问题的示例:示例 .txt 文件

column1 column2 column3 column4 column5
1   2   3   4   5
2   5   4   6
4   4   6   4   
4   5   6   4   6

SQL 创建表和批量插入

CREATE TABLE test (
[column1] varchar(MAX) NULL,
[column2] varchar(MAX) NULL,
[column3] varchar(MAX) NULL,
[column4] varchar(MAX) NULL,
[column5] varchar(MAX) NULL
)

BULK INSERT test
FROM 'c:\temp\testimport.txt'
WITH
(
    FIRSTROW = 2,
    FIELDTERMINATOR = '\t',
    ROWTERMINATOR = '\r'
);

真正奇怪的是,我可以使用数据导入向导,它可以完美地导入数据,没有任何问题,并且可以很好地处理列缺少选项卡的问题。但我不知道巫师在幕后做了什么来实现这一点。我很想拥有它用来创建表格并进行插入的代码,因为这可能会回答我的问题。最终我无法使用该向导,因为这最终将成为自动化任务的一部分,我将针对 SQL Server Express 数据库运行多个名称不同但列标题相同的文件。

也许批量插入不是这里的方法?或者有一些明显的东西我错过了,其他人可能知道他们的头脑。无论哪种方式,所有帮助都表示赞赏并提前致谢。


正如 Tim H 所建议的,我已经尝试过创建格式文件来容纳数据。目前的结果如下。

使用 bcp temp.dbo.test format nul -x -f test_format.xml -n -T

生产

<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="CharPrefix" PREFIX_LENGTH="2" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="2" xsi:type="CharPrefix" PREFIX_LENGTH="2" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="3" xsi:type="CharPrefix" PREFIX_LENGTH="2" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="4" xsi:type="CharPrefix" PREFIX_LENGTH="2" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="5" xsi:type="CharPrefix" PREFIX_LENGTH="2" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
 </RECORD>
 <ROW>
  <COLUMN SOURCE="1" NAME="column1" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="2" NAME="column2" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="3" NAME="column3" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="4" NAME="column4" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="5" NAME="column5" xsi:type="SQLVARYCHAR"/>
 </ROW>
</BCPFORMAT>

按原样使用这个临时文件会产生......

消息 4866,第 16 级,状态 7,第 31 行 批量加载失败。数据文件中第 1 行第 1 列的列太长。请验证是否正确指定了字段终止符和行终止符。

我尝试编辑 XML 以使其正常工作.....

<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="CharTerm" TERMINATOR="\t" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="2" xsi:type="CharTerm" TERMINATOR="\t" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="3" xsi:type="CharTerm" TERMINATOR="\t" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="4" xsi:type="CharTerm" TERMINATOR="\t" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
  <FIELD ID="5" xsi:type="CharTerm" TERMINATOR="\r\n" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
 </RECORD>
 <ROW>
  <COLUMN SOURCE="1" NAME="column1" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="2" NAME="column2" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="3" NAME="column3" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="4" NAME="column4" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="5" NAME="column5" xsi:type="SQLVARYCHAR"/>
 </ROW>
</BCPFORMAT>

确实插入了数据,但不幸的是仍然会产生相同的混乱插入,并且在同一行中有重叠的行。

【问题讨论】:

  • 你可以试试KEEPNULLS 选项吗?另一种选择是使用格式文件,您可以先生成格式文件,然后使用它来导入可能会起作用。
  • 已经尝试过KEEPNULLS,结果是一样的。至于格式文件,我读过一点,但没有经验。我只是想如果向导可以完成导入,那么一定有一种干净的方法可以通过查询来完成。
  • 您已经标记了 SQL Server MySQL?如果这不是故意的,请删除一个。
  • 意外删除,谢谢指出。

标签: sql-server bulkinsert flat-file


【解决方案1】:

您可以控制源文件吗?如果不是,每列的宽度是固定宽度还是可变宽度?我知道您的创建表示例使用varchar(max)'s。 SQL Server 中的批量插入功能允许您使用格式文件,该格式文件可以更好地按列定义预期输入的格式,包括列是否可以为空。 Microsoft 的批量插入文档实际上非常有用 (https://docs.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15),尤其是页面末尾的格式化文件链接。

此页面 (https://docs.microsoft.com/en-us/sql/relational-databases/import-export/keep-nulls-or-use-default-values-during-bulk-import-sql-server?view=sql-server-ver15) 直接处理空值,这将是您的困境。

【讨论】:

  • 列的宽度是可变的。这些是由将各种数据写入其中一些列的机器生成的平面文件。因此,为什么我使用 VARCHAR(MAX) 以便它可以处理机器扔进一列的任何东西。我可能会摆脱 VARCHAR(100)。至少那是我在向导中所做的。虽然我还没有浏览所有的 .txt 文件,看看是否有任何东西会超过 100。所以我玩得很安全。
  • 这些列的最终目的地是什么?如果它期望的字段不超过 100 个字符,那么最好的选择是尽早将其限制为 varchar(100),这样您就不会携带超出应用程序实际使用的数据的额外数据。我承认,这本身仍然无法解决您当前的问题。我在不久前做的一个地理编码项目中遇到了类似的问题。我正在尝试从中找到我的代码,看看我是否能给你一个关于我如何克服它的准确答案。
  • 因此,根据 MS 的说法,您绝对必须使用格式文件。从他们的文档中:“如果出现以下情况,则需要格式文件:...数据文件具有与目标表的列不同的字段数;例如:-目标表包含至少一个定义了默认值的列或 NULL 是允许的。”
  • 要创建格式文件,请尝试在命令行窗口中使用:bcp MyTestDatabase.dbo.test format nul -x -f test_format.xml -n -T 其中“MyTestDatabase.dbo.test”是您在本地 SQL 实例上的问题表的示例版本服务器,其中一个或多个字段包含空值。
  • 创建格式文件后,使用命令BCP CMM_DATA.dbo.test IN C:\temp\testimport.txt -f testimport.xml -T会产生以下错误。 Starting copy... SQLState = S1000, NativeError = 0 Error = [Microsoft][ODBC Driver 13 for SQL Server]Attempt to bulk-copy an oversized column to the SQL Server
【解决方案2】:

更好的答案是将以下内容添加到您的 BULK INSERT...WITH 语句中:KEEPNULLS。正如你所期望的那样:它保留空值而不是扔掉它们。默认情况下,批量插入实用程序会抛出空值。

【讨论】:

  • 将 KEEPNULLS 添加到上述批量导入语句仍然会产生相同的缺陷结果。
  • 所以不要尝试ROWTERMINATOR = '\r',而是尝试ROWTERMINATOR = '\r\n'
  • \r\n 生成插入的零行。
【解决方案3】:

从来没有从 SQL express 中找到直接的解决方案。我最终使用 PowerShell 脚本来解决这个问题。 Import-CSV 从文件中统一提取数据,没有问题。不知道为什么,但它处理数据的能力比 SQL 好得多。从那里我为每一行使用变量,并使用 Invoke-SQLCmd 和一些 SQL 脚本将它们导入数据库。像魅力一样工作。由于此过程都在本地服务器上,因此无需担心任何安全问题,因此这是一个可以接受的解决方案。再次感谢所有建议和帮助。

【讨论】:

    猜你喜欢
    • 2013-12-27
    • 1970-01-01
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多