在 SQL Server 中批量插入部分引用的 CSV 文件

Posted

技术标签:

【中文标题】在 SQL Server 中批量插入部分引用的 CSV 文件【英文标题】:Bulk Insert Partially Quoted CSV File in SQL Server 【发布时间】:2012-10-05 19:26:09 【问题描述】:

我正在尝试导入正确引用的 CSV 文件,这意味着数据仅在包含逗号时才会被引用,例如:

41, Terminator, Black
42, "Monsters, Inc.", Blue

我观察到第一行正确导入,但第二行错误,表明引用的逗号被视为字段分隔符。

我看过类似这样的建议

SQL Bulk import from CSV

更改字段终止符

FIELDTERMINATOR='","'

但是,我的 CSV 文件只引用需要它的字段,所以我不相信这个建议会起作用。

SQL Server 的 BULK IMPORT 语句可以导入正确引用的 CSV 文件吗?怎么样?

【问题讨论】:

无法完成。 SQL Server 导入方法(BCP 和 BULK INSERT)不理解引用。 这是一个类似的帖子,有更多选项***.com/questions/782353/… 引用不正确。根据 RFC4180,引号应该围绕所有字段或无字段。部分引用的 CSV 无效,无法使用 BULK IMPORT 加载。它们可以在 Excel、PowerShell 甚至 OPENROWSET 中打开,但不能直接批量导入。 @GeoffGriswald 我不同意你关于 RFC4180 规范的结论。第 2.5 节和第 2.6 节显示了混合使用引号和非引号的示例,ABNF 语法似乎表明任何给定的字段都可以被引用或不被引用。但是,在可能的情况下始终引用每个字段并使用 Dominix 的答案是可靠的建议。 Eric,像许多人一样,您误解了这些示例。 2.5 显示了 2 个示例,一个完全引用,一个未引用。 2.6 显示了 2 个示例,一个完全引用的跨两行(以证明一个字段包含 CRLF)和一个未引用的跨行。没有部分引用的 CSV 规范,RFC4180 也没有提供。这纯粹是通过 Excel 及其对 CSV 格式的历来糟糕的处理(这是故意进行的,以推广“.XLS”)产生的。 Excel 2019 终于提供了正确处理 CSV 文件的选项,但损坏已经造成。 【参考方案1】:

现在看来这适用于部分引用的 csv 文件,只要您包含 FORMAT='CSV' 在选项中。一旦我添加了这个,它就可以按预期使用部分引用的文件。

【讨论】:

【参考方案2】:

我遇到了同样的问题,但是,它对我来说是通过以下设置工作的:

bulk insert schema.table
from '\\your\data\source.csv'
with (
datafiletype = 'char'
,format = 'CSV'
,firstrow = 2
,fieldterminator = '|'
,rowterminator = '\n'
,tablock
)

我的 CSV 文件如下所示:

"col1"|"col2"
"val1"|"val2"
"val3"|"val4"

我的问题是,我之前将 rowterminator 设置为 '0x0a',但它不起作用。一旦我把它改成'\n',它就开始工作了......

【讨论】:

【参考方案3】:

被同样的人蛰了:)

我将这个逻辑封装到一个函数中,以清理已经导入的数据

DECLARE @str NVARCHAR(MAX);
DECLARE @quote_identifier NVARCHAR(MAX);

SET @quote_identifier = N'"';

SET @str = N'"quoted stuff"';

SELECT IIF(
           LEFT(@str, 1) = @quote_identifier
           AND RIGHT(@str, 1) = @quote_identifier,
           SUBSTRING(@str, DATALENGTH(@quote_identifier), LEN(@str) - DATALENGTH(@quote_identifier)),
           @str);

【讨论】:

【参考方案4】:

我知道这是一个老话题,但这个功能现在已经从 SQL Server 2017 开始实现了。您要查找的参数是 FIELDQUOTE=,默认为 '"'。在https://docs.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql?view=sql-server-2017 上查看更多信息

【讨论】:

SQL Server 花了很长时间才添加适当的支持 :-) 哦,那么也许我可以将新的 bcp.exe 与 SQL Server 2012 一起使用,如果它也支持这样的参数(而不仅仅是 BULK COPY 指令 - 或其他任何东西)。编辑:不,没有参数。也许有一个全局(不是每列)格式文件指令,希望如此。 不适用于部分引用的文件,例如 OPs 示例中 如果我最初的问题没有混合引用和不引用,我会接受这个。在可能的情况下始终引用每个字段并使用这种方法当然是合理的建议。 我认为“总是”引用每个字段不是正确的方法。例如,"" 表示 “我知道字符串有 0 个字符”,而没有引号表示 “我不知道”。两者是不同的陈述,模糊这条线会给数据完整性带来风险。【参考方案5】:

确保您已启用 TextQualified 选项并将其设置为 "

【讨论】:

这是正确答案。如果您设置了文本限定符,可能的。这是在导入和导出向导中选择数据源时完成的 ...这仅适用于 BULK COPY 命令而不适用于 bcp.exe? :-( 而且我必须重写整个命令? :-( 或者,如果我不需要为每一列指定一个设置,我可能只需要其中一个神秘的格式文件(希望如此!我正在尝试使用许多表都使用相同的命令)。 我的意思是这是一个的答案。对于加载数十或数百个文件时的真正批量操作,通常不能使用导入和导出向导。【参考方案6】:

我在这个问题上花了半天时间。最好使用 SQL Server 导入和导出数据向导进行导入。该向导中有一个设置可以解决此问题。详细截图在这里:https://www.mssqltips.com/sqlservertip/1316/strip-double-quotes-from-an-import-file-in-integration-services-ssis/谢谢

【讨论】:

【参考方案7】:

我遇到了同样的问题,我不想走 SSIS 路线,所以我找到了一个 PowerShell 脚本,它易于运行并处理该特定字段中带逗号的引号的情况:

PowerShell 脚本的源代码和 DLL:https://github.com/billgraziano/CsvDataReader

这是一个解释用法的博客: http://www.sqlteam.com/article/fast-csv-import-in-powershell-to-sql-server

【讨论】:

PowerShell 方式确实解决了这个问题,因为 import-csv 本身足够聪明,可以处理包含字段终止符的引用字段。恕我直言,这是一个不错的解决方案 我不知道“足够聪明”是否是正确的词。它可以容忍格式错误的 .CSV 文件,以便能够处理部分引用的文件。可以肯定的是,这是一个出色的解决方案。【参考方案8】:

我遇到了同样的问题,数据只是偶尔双引号一些文本。 我的解决方案是让 BULK LOAD 导入双引号,然后对导入的数据运行 REPLACE。

例如:

批量插入 CodePoint_tbl 来自“F:\Data\Map\CodePointOpen\Data\CSV\ab.csv” with (FIRSTROW = 1, FIELDTERMINATOR = ',', ROWTERMINATOR='\n');

更新 CodePoint_tbl 设置邮政编码 = 替换(邮政编码,'"','') 其中 charindex('"',Postcode) > 0

为了减少编写 REPLACE 脚本的痛苦,只需从以下结果中复制并粘贴您需要的内容:

select C.ColID, C.[name] as Columnname into #Columns
from syscolumns C
join sysobjects T on C.id = T.id
where T.[name] = 'User_tbl'
order by 1;

declare @QUOTE char(1);
set @QUOTE = Char(39);
select 'Update User_tbl set '+ColumnName+'=replace('+ColumnName+','
 + @QUOTE + '"' + @QUOTE + ',' + @QUOTE + @QUOTE + ');
GO'
from #Columns
where ColID > 2
order by ColID;

【讨论】:

这是双引号,而不是 PO 提到的引号内的逗号。【参考方案9】:

对此还有另一种解决方案。

通过编辑 fmt 文件,将引号视为字段分隔符的一部分。

您可以查看此以获取更多信息:

http://blogs.msdn.com/b/sqlserverfaq/archive/2010/02/04/how-to-remove-unwanted-quotation-marks-while-importing-a-data-file.aspx

以上链接摘录:

删除引号的唯一方法是修改在导入操作期间指定的列分隔符。这里唯一的缺点是,如果您检查要插入的数据,您会很快意识到每列的列分隔符不同(上面突出显示的分隔符)。

因此,要为每列指定不同的列分隔符,如果您打算使用批量插入或 BCP,则需要使用格式文件。如果为上面的表结构生成一个格式文件,会如下:

9.0
3
1       SQLCHAR       0       5       "\t"     1     FName              SQL_Latin1_General_CP1_CI_AS
2       SQLCHAR       0       5       "\t"     2     LName              SQL_Latin1_General_CP1_CI_AS
3       SQLCHAR       0       50      "\r\n"   3     Company            SQL_Latin1_General_CP1_CI_AS

修改格式文件以表示每列的正确列分隔符。要使用的新格式文件如下所示:

9.0
4
1       SQLCHAR       0       0     "\""      0     FIRST_QUOTE      SQL_Latin1_General_CP1_CI_AS
2       SQLCHAR       0       5     "\",\""   1     FNAME               SQL_Latin1_General_CP1_CI_AS
3       SQLCHAR       0       5     "\",\""   2     LNAME            SQL_Latin1_General_CP1_CI_AS
4       SQLCHAR       0       50    "\"\r\n"  3     COMPANY          SQL_Latin1_General_CP1_CI_AS

【讨论】:

@parakmiakos:答案已更新为摘要。 CSV 文件:"field"",""","second_field",这意味着双引号整个字段和双引号 ("") 在字段内的双引号。如果您知道使用 "," 作为字段 1 和 2 之间的分隔符,您将获得 3 个字段,尽管您只需要 2 个字段。我错了吗? 当在最后一行插入没有空行的数据时,这会使双引号显示在最后一行的最后一列上。我还没有找到解决这个问题的方法... 批量插入完成后,只需将双引号替换为空即可【参考方案10】:

您还可以考虑将 OpenRowSet 与 CSV 文本文件数据提供程序一起使用。

这应该适用于任何版本的 SQL Server >= 2005,尽管您需要启用该功能。

http://social.msdn.microsoft.com/forums/en-US/sqldataaccess/thread/5869d247-f0a0-4224-80b3-ff2e414be402

【讨论】:

【参考方案11】:

不幸的是,SQL Server 将引用的逗号解释为分隔符。这适用于 BCP 和批量插入。

来自http://msdn.microsoft.com/en-us/library/ms191485%28v=sql.100%29.aspx

如果数据中出现终止符,则将其解释为 终止符,而不是数据,并且该字符之后的数据是 解释为属于下一个字段或记录。所以, 仔细选择你的终结者,以确保它们永远不会出现 在您的数据中。

【讨论】:

自 SQL Server 2017 起,我们有了 FORMAT='CSV' 选项,可用于 BULK INSERT。如果您的 CSV 文件被完全引用,这些字段现在可以包含逗号。【参考方案12】:

根据 CSV 格式规范,我认为数据是否正确引用并不重要,只要它符合规范即可。如果正确实施,解析器应处理过多的引号。 FIELDTERMINATOR 应该是逗号,ROWTERMINATOR 是行尾 - 这表示标准 CSV 文件。您是否尝试使用这些设置导入数据?

【讨论】:

我的输入数据中没有 excessive 引号,只有引用恰好包含逗号的字段所需的精确引号数量。当导入正确引用 CSV 的数据时,FIELDTERMINATOR 是一个逗号,而 ROWTERMINATOR 是正确的,批量导入会以某种方式阻塞,表明它不理解引用的逗号。 @EricJ.:我理解你的情况。但我不知道微软使用 String.Split(",") 实现了他们的批量导入,这是初学者程序员认为 CSV 工作的方式。微软的耻辱。您有两个选择:重新解析 CSV 并添加额外的引号,然后使用您提到的方法或创建一个程序来生成和执行基于 CSV 文件的 INSERT 语句。如果您选择最后一个,请确保您不要对它们进行事务化。 问题是人们不明白应该如何创建 .CSV 文件。有两个选项:全引号(所有字段都加引号)或非引号(无引号字段)。之所以出现这种部分引用 CSV 文件的想法,是因为 Microsoft Excel 经常以这种方式创建它们,但这不是一种有效的格式,并且不受 Microsoft Access 以外的任何数据库引擎的支持。

以上是关于在 SQL Server 中批量插入部分引用的 CSV 文件的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 批量插入数据的两种方法 - 转

从 Excel / CSV 批量插入到 SQL Server

SQL Server 批量插入是事务性的吗?

批量插入、SQL Server 2000、unix 换行符

在 SQL Server CE 中批量插入

JPA SQL Server 批量插入