【问题标题】:SQL Server bulk insert doesn't recognize double quotes as fieldquoteSQL Server 批量插入不能将双引号识别为字段引用
【发布时间】:2020-03-03 11:43:04
【问题描述】:

我正在尝试在 SQL Server 2017 14.0.1000.169 中批量插入文件。我想在文件到达时准确获取文件,将其保存到所需位置,然后运行批量插入查询根本无需修改文件。我很难让脚本识别并忽略文本文件中的双引号,除非我手动将行尾从 Unix 更改为 Windows。我在这里和之外阅读了很多主题,讨论了与这个主题接近的主题,唉,没有一个能回答我的问题:

如何批量插入带有 Unix 行结尾的文件,而不是以双引号结尾?

我的文件如下所示:

"Report Name","Daily Extract (ID: 111111)"
"Date/Time Generated(UTC)","01-Mar-2020 15:08:51"
"Workspace Name","Company (ID: 22222)"
"Account Name","Client Account"
"Date Range","01-Jan-2019 - 29-Feb-2020"

"Dimension 1","Dimension 2","Dimension 3","Dimension 4","Dimension 5","Dimension 6","Dimension 7","Dimension 8","Dimension 9","Dimension 10","Dimension 11","Dimension 12","Dimension 13","Dimension 14","Dimension 15","Dimension 16","Dimension 17","Metric 1","Metric 2","Metric 3","Metric 4","Metric 5","Metric 6","Metric 7","Metric 8","Metric 9","Metric 10","Metric 11","Metric 12"
"string","string","date as string","string","string","string","string","string","string","string","string","string","string","string","string","string","string","bigint","bigint","decimal","decimal","decimal","bigint","decimal","decimal","bigint","decimal","bigint","bigint"

我使用的查询如下:

DROP TABLE IF EXISTS Table
GO

CREATE TABLE [dbo].[Table](
    [Dimension 1] [varchar] (255) NULL,
    [Dimension 2] [varchar] (255) NULL,
    [Dimension 3] [varchar] (255) NULL,
    [Dimension 4] [varchar]  (255) NULL,
    [Dimension 5] [varchar] (255),
    [Dimension 6] [varchar] (255) NULL,
    [Dimension 7] [varchar] (255) NULL,
    [Dimension 8] [varchar] (255) NULL,
    [Dimension 9] [varchar] (1000) NULL,
    [Dimension 10] [varchar] (255) NULL,
    [Dimension 11] [varchar] (255) NULL,
    [Dimension 12] [varchar] (255) NULL,
    [Dimension 13] [varchar] (1000) NULL,
    [Dimension 14] [varchar] (1000) NULL,
    [Dimension 15] [varchar] (1000) NULL,
    [Dimension 16] [varchar] (1000) NULL,
    [Dimension 17] [varchar] (1000) NULL,
    [Metric 1] [varchar] (50) NULL,
    [Metric 2] [varchar] (50) NULL,
    [Metric 3] [varchar] (50) NULL,
    [Metric 4] [varchar] (50) NULL,
    [Metric 5] [varchar] (50) NULL,
    [Metric 6] [varchar] (50) NULL,
    [Metric 7] [varchar] (50) NULL,
    [Metric 8] [varchar] (50) NULL,
    [Metric 9] [varchar] (50) NULL,
    [Metric 10] [varchar] (255) NULL,
    [Metric 11] [varchar] (50) NULL,
    [Metric 12] [varchar] (50) NULL
) ON [PRIMARY]
GO

BULK
INSERT Table
FROM 'C:\Users\username\Folder\File.csv'
WITH
(
--FORMAT = 'CSV',
DATAFILETYPE = 'char',
FIELDTERMINATOR = ',',
--ROWTERMINATOR = '\n',
ROWTERMINATOR = '0x0a',
FIRSTROW = 7,
--FIELDQUOTE = '"'
FIELDQUOTE = '0x22'
)
;

正如您在上面看到的,我将所有内容都作为 varchar 导入。最初我只将它用于一个指标(由于供应端的数据质量问题),因为我完全打算在文件加载后纠正每个瑕疵。遇到困难,但是我已将所有指标设置为 varchar,所以至少文件会加载,我可以看到它的样子并进一步挖掘。

到目前为止,我尝试过以下方法:

  • 在 Sublime 中打开文件,删除前 7 行,将行尾更改为 Windows 并保存 - 这适用于我已注释掉的行,即 FORMAT 而不是 DATAFILETYPE,\n 而不是 0x0a,FIELDQUOTE 为“
  • 保持文件不变并使用双引号而不是 0x22 运行上述脚本 - 这也可以,但最终结果是每个值都在双引号中

  • 保持文件不变并按原样运行上述脚本(即,使用 0x22 作为 FIELDQUOTE) - 再次有效,但处处使用双引号

到目前为止,我尝试过的所有其他事情都会导致各种错误,这些错误都会导致相同的两件事:要么我不能使用 FORMAT = 'CSV'(如果我将 Unix 行结尾留在其中​​),要么当我尝试将指标加载为浮点数时,由于双引号而出错。

我暂时有一个解决方法(我可以删除双引号并在加载内容后转换字段),但是我想知道是否可以将该步骤集成到批量插入中(就像我在加载了带有 Windows 结尾的文件)。

注意我知道 FIELDQUOTE 出现的时间不长,但是,根据 Microsoft,它应该适用于我的构建:

"FIELDQUOTE = 'field_quote' 适用于:SQL Server 2017 (14.x) CTP 1.1。指定将用作 CSV 文件中的引号字符的字符。如果未指定,将使用引号字符 (") 作为 RFC 4180 标准中定义的引号字符。”

我忘记透露什么了吗?如果没有,有什么我可能忽略的想法吗?

提前致谢!

【问题讨论】:

  • 您在示例中注释掉了FORMAT = 'CSV'
  • 在示例下,您可以了解原因:被注释掉的位是在带有 Windows 行尾的文件上运行的,而正在使用的位是在 Unix 行尾上运行的。一个例外是 FIELDQUOTE,我已经尝试了 Unix 行结尾的两个版本(" 和 0x22)。希望这有意义吗?
  • 另外,我尝试使用 Unix 结尾的 FORMAT,但它抛出了一个错误。我在 SO 上的另一个线程中读到它(我现在无法找到我的生命),OP 应该使用 DATAFILETYPE 代替,我尝试过并且它有效,因此进行了替换。不过,我可能还缺少其他东西,我不知道。

标签: sql-server unix bulkinsert double-quotes line-endings


【解决方案1】:

好的。这里最大的问题是你的文件。首先,由于顶部的行,该文件没有RFC 4180。这让人头疼。

接下来是关于FIRSTROW重要警告:

在跳过行时,SQL Server 数据库引擎仅查看字段终止符,而不验证跳过行的字段中的数据。

注意这里说的是字段终止符 不是行终止符。这是第二个问题。对于您的数据,您一开始就有这个:

"Report Name","Daily Extract (ID: 111111)"
"Date/Time Generated(UTC)","01-Mar-2020 15:08:51"
"Workspace Name","Company (ID: 22222)"
"Account Name","Client Account"
"Date Range","01-Jan-2019 - 29-Feb-2020"
<-- Blank Line -->

这是 6 个字段终止符和 6 个行终止符。

接下来,CSV 文件中的列比Table 表中的列多。 Table 没有 Dimension 17 列。

在添加了这个缺失的列之后,我设法得到了这个结果,我相信你正在使用以下结果:

BULK INSERT [Table]
FROM '/tmp/YourFile2.txt'
WITH (FIELDTERMINATOR = ',',
      ROWTERMINATOR = '\n',
      FIRSTROW = 2,
      FORMAT = 'CSV',
      FIELDQUOTE = '"');

这在表格中插入了 1 行。

【讨论】:

  • Grand,在这里回复的内容相当多 :) 所以,Dim 17 是一个 mt,我已将其添加到原始帖子中以避免混淆。原始查询实际上确实有正确数量的列。 (由于我的合同、保密协议等原因,请多多包涵。我必须在将所有内容发布到任何地方之前对所有内容进行脱敏处理,而我只是错过了此处代码中的一列。)
  • 关于前几行:当我跳过前 6 行时,它对我来说很好,我刚刚尝试过。与上面的代码相同,除了 FIRSTROW 是 7。这仅在行尾是 Windows 的行尾(即您正在使用的 \n )时才有效,而原始文件中的情况并非如此。如果你还记得的话,我的目标是在加载文件之前完全不必手动处理文件,换句话说:1. 保留前 6 行中的废话并简单地跳过它(检查),2. 保留 Unix 行结尾并取消双引号(要么/或检查,一起不)。
  • “这仅在行尾是 Windows 的情况下才有效” 我正在使用的测试文件仅使用 LF 行尾。当我使用ROWTERMINATOR = '\n' 时,您可以看到这一点,而且,我实际上是在 Unix 环境中测试它。我故意确保我使用的是LF 文件。
  • 嗯.. 这很奇怪,\n 对我来说根本不适用于 Unix 文件。只是为了确认,环境一直是 Windows,只是文件发生了变化。奇怪的是,当我使用 Windows 行结尾 (LF CR) 保存文件时,\n 无需 \r 就可以正常工作。另外,当我说它只适用于 Windows 时......从字面上看,当我用 Unix 行结尾保存文件时,\n 不起作用。我必须将其更改为 0x0a。在这种情况下,FORMAT 也不起作用。也许我可以在某个地方上传两个文件,你可以用它们进行测试,因为我怀疑你的文件不同?
  • 我找到了文章(github.com/Microsoft/sql-server-samples/issues/408),在它的背面我用DATAFILETYPE替换了FORMAT。我还清理了我的尝试并发现了这一点:该文件的 Windows 版本与后者运行良好(不管 RFC4180 不合规性如何),但随后 FIELDQUOTE 被忽略了。回顾一下,我要么必须在加载前删除脏行,要么在加载后删除双引号。我确实想知道我是否可以在加载时同时执行这两项操作...感谢您花时间研究它@Larnu!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-21
  • 2019-07-20
  • 2023-03-25
  • 1970-01-01
相关资源
最近更新 更多