将数据导出到 CSV 平面文件时如何解决嵌入的文本限定符问题?

Posted

技术标签:

【中文标题】将数据导出到 CSV 平面文件时如何解决嵌入的文本限定符问题?【英文标题】:How to fix the embedded text qualifier issue while exporting data to CSV flat file? 【发布时间】:2013-01-26 18:10:35 【问题描述】:

###RFC 4180:

RFC 4180 定义 Common Format and MIME Type for Comma-Separated Values (CSV) FilesRFC 4180 的要求之一如下所述。这是 RFC 链接中的 #7 点。

If double-quotes are used to enclose fields, then a double-quote
appearing inside a field must be escaped by preceding it with
another double quote.  For example:

"aaa","b""bb","ccc"

###SQL Server 2000:

DTS Export/Import Wizard in SQL Server 2000 似乎符合上述标准,尽管 RFC 4180 本身似乎仅在 2005 年 10 月发布。我正在使用下面所述的 SQL Server 2000 版本。

Microsoft SQL Server  2000 - 8.00.2039 (Intel X86) 
May  3 2005 23:18:38 
Copyright (c) 1988-2003 Microsoft Corporation
Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)

###SQL Server 2012:

SQL Server Import and Export Wizard in SQL Server 2012 不会根据 RFC 4180 中定义的标准将数据从表导出到 CSV 文件。我正在使用以下说明 SQL Server 2012 版本。

Microsoft SQL Server 2012 - 11.0.2316.0 (X64) 
Apr  6 2012 03:20:55 
Copyright (c) Microsoft Corporation
Enterprise Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1) (Hypervisor)

###问题模拟:

这是我在 SQL Server 2000SQL Server 2012 中运行的示例。我运行以下查询来创建一个表并插入几条记录。 ItemDesc 列包含带有双引号的数据。我的目的是使用它们内置的导出数据向导从这两个 SQL Server 版本中导出数据,并比较生成的 CSV 文件。

CREATE TABLE dbo.ItemInformation(
    ItemId nvarchar(20) NOT NULL,
    ItemDesc nvarchar(100) NOT NULL
) 
GO

INSERT INTO dbo.ItemInformation (ItemId, ItemDesc) VALUES ('100338754', 'Crown Bolt 3/8"-16 x 1" Stainless-Steel Hex Bolt');
INSERT INTO dbo.ItemInformation (ItemId, ItemDesc) VALUES ('202255836', 'Simpson Strong-Tie 5/8" SSTB Anchot Bolt');
INSERT INTO dbo.ItemInformation (ItemId, ItemDesc) VALUES ('100171631', 'Grip-Rite #11 x 1-1/2" Electro-Galvanized Steel Roofing Nails');
INSERT INTO dbo.ItemInformation (ItemId, ItemDesc) VALUES ('202210289', 'Crown Bolt 1/2" x 3" "Zinc-Plated" Universal Clevis Pin');
INSERT INTO dbo.ItemInformation (ItemId, ItemDesc) VALUES ('100136988', 'Tapcon 3/16" x 1-3/4" Climaseal Steel "Flat-Head" Phillips Concrete Anchors (75-Pack)');
INSERT INTO dbo.ItemInformation (ItemId, ItemDesc) VALUES ('203722101', 'KwikTap 3/16" x 2-1/4" "Flat-Head" Concrete Screws (100-Pack)');
GO

SQL Server 2000 中的 DTS Export/Import Wizard 上,我使用以下设置将数据导出到 CSV 文件。我以 SQLServer2000_ItemInformation.csv 的名称保存了文件。

SQL Server 2012 中的 SQL Server Import and Export Wizard 上,我使用以下设置将数据导出到 CSV 文件。我以 SQLServer2012_ItemInformation.csv 的名称保存了文件。

这是使用 Beyond Compare 对两个文件进行的比较。左侧包含SQL Server 2000 生成的文件,右侧包含SQL Server 2012 生成的文件。您会注意到来自SQL Server 2000 的左侧文件包含额外的双引号以补偿数据列中嵌入的引号。这符合RFC 4180 中指定的标准,但SQL Server 2012 生成的文件中明显缺少它

###网络搜索:

我在网上搜索了这个错误并找到了以下链接。以下是 Microsoft Connect 上的错误报告。所有这些问题似乎都与导入文件有关,但与导出数据无关。所有这些错误都已作为Fixed 关闭。

SSIS flat file parser does not read Column delimiters embedded in text data Flat File Connection Manager not handling Text Delimiters in CSV Files Embedded quotes in Flat File Import fails BUG: Flat File Connection Manager: multiple-character text qualifier does not load all data

MSDN 博客上的以下帖子指出,SQL Server 2012 中已对 Flat file source supports embedded qualifiers and a variable number of columns per row 进行了更改

SSIS - What’s New in SQL Server Denali

MSDN 博客上的另一篇文章在Embedded Qualifiers 部分下也有相同的说明。

Flat File Source Changes in Denali

我知道的###Workaround:

我知道解决问题的方法是编写一个查询,用两个双引号 ("" 替换列数据中的所有双引号 (") ) 以便导出的文件以正确的嵌入限定符数据结束。这样可以避免直接从表中提取数据。

###我的问题:

我不知道SQL Server 2012 中是否真正解决了这个问题。此问题是否仅针对具有嵌入文本限定符的 importing 文件和 exporting 数据到 CSV 的 not 得到修复?

很可能,我显然做错了什么,错过了显而易见的事情。有人可以向我解释一下我在这里做错了什么吗?

###Microsoft 连接:

我已在 Microsoft Connect 网站上提交了一份错误报告以获取他们的反馈。这是错误报告的链接。如果您同意这是一个错误,请访问以下链接在Microsoft Connect 网站上投票。

Embedded text qualifier during export to CSV does not conform to RFC 4180

【问题讨论】:

我花了一些时间研究和复制,它似乎只是一个错误。 对我来说也像一个错误。我没有意识到 rfc 已经发布,但自从我解析 csv 以来已经有一段时间了。我总是把 ", 和 "\n 作为一个块的结束,并接受该字段中的任何 " 或 ""。但这对你没有帮助:( 遗憾的是,SQL Server 的 BI 工具似乎并没有得到太多的开发关注。过去,我对嵌入式分隔符有很多痛苦,并切换到 | (管道)作为我们导入/导出数据的分隔标准已经减轻了很多 - 您也许可以做类似的事情。我在 connect 方面的经验是,如果你不能让几千人投票支持你的 bug,那么它就不会去任何地方。另一种选择是打开支持事件,但这仍然不确定,即使在最好的情况下,您仍可能需要等待数月才能得到解决。 观察:尽管行业中的参与者向您上面提到的 RFC 中概述的普遍接受的最佳实践迈进无疑是件好事。但是,仅仅因为存在这一点,并不意味着任何给定的供应商都遵循该约定。也许他们应该这样做,因此他们可能会表示“我们的产品支持 CSV 文件的 RFC 4180 约定”,这将使我们中的许多人在必须处理 CSV 文件时更加快乐。太多供应商这样做了,但正如您的研究表明的那样,他们仍然倾向于这样做。 为什么不用其他软件导出呢? 【参考方案1】:

我不会提供这个答案,除非你非常努力地记录它并且一个月后没有答案。所以,就这样吧。您唯一的选择似乎是更改数据或更改工具。

很可能,我显然做错了什么,错过了显而易见的事情。有人可以向我解释一下我在这里做错了什么吗?

当工具坏了而供应商不在乎时,继续尝试是错误的。是时候切换了。您投入了大量精力来研究它是如何被破坏的,并证明它不仅违反了 RFC,而且违反了该工具自己的先前版本。你还需要多少证据?

CSV 也是船锚。如果可以选择,最好使用普通的分隔文件格式。对于很多应用程序,制表符分隔是好的。最好的分隔符 IMO 是“\”,因为该字符在英文文本中没有位置。 (另一方面,它不适用于包含 Windows 路径名的数据。)

CSV 作为交换格式有两个问题。首先,它不是那么标准。无论 RFC 怎么说,不同的应用程序识别不同的版本。其次(和相关的)是它不构成 CS 术语中的常规语言,这就是为什么它不能被解析为正则表达式的原因。与^([^\t]*\t)*[\t]*$ 比较以获取制表符分隔的行。 CSV 定义的复杂性的实际含义是(见上文)处理它们的工具相对缺乏以及它们不兼容的趋势,特别是在凌晨。

如果您让 CSV 和 DTS 启动,您有很好的选择,其中之一是 bcp.exe。它非常快速且安全,因为微软多年来一直没有尝试对其进行更新。我对 DTS 了解不多,但如果您必须将其用于自动化,IIRC 有一种方法可以调用外部实用程序。但请注意,bcp.exe 不会可靠地将错误状态返回给 shell。

如果您决定使用 DTS 并坚持使用 CSV,那么剩下的最佳选择就是编写一个视图,为它准备适当的数据。如果回到那个角落,我会创建一个名为“DTS2012CSV”的模式,这样我就可以写select * from DTS2012CSV.tablename,让任何关心它的人都有机会理解它(因为你会记录它,不会你,在视图文本中的 cmets?)。如果需要,其他人可以将其技术复制到其他损坏的提取物中。

HTH。

【讨论】:

感谢您的回复。我知道这个特定的功能在 SSIS 2005 和 2008 R2 中被破坏了。我在上述所有链接中读到这已在 SSIS 2012 中修复,但我没有发现任何证据。看起来微软修复了导入文件,这也是一个已知问题,但仍然没有修复导出。我把它贴在这里看看我是否忽略了任何明显的东西。问题不在于 DTS,而在于 SSIS。不,它们不一样。 SSIS 与 DTS 大不相同,在我看来要好得多。 |(管道)字符是一个更好的分隔符 IMO。它几乎从未在数据中的任何地方使用过。从这个意义上说,在数据中找到| 比找到 ``:)【参考方案2】:

我知道这已经有两年了,但我现在也遇到了这个问题,因为我们需要使用 SQL Server 2008 来签订合同(不要问)。阅读完这个问题后,我意识到我需要做替换建议,但是当我在查询中去做时,我遇到了截断问题,因为在查询本身中使用 replace() 函数会将文本转换为默认为 varchar(8000)。

但是,我发现我可以在 DB Source 和 Flat File 对象之间使用 Derived Column 步骤来做同样的事情。例如,我有一个名为“short_description”的列,其中可能包含引号,所以我只是使用以下函数作为表达式,并在派生列中选择了“替换 short_description”:

REPLACE(short_description,"\"","\"\"")

这似乎已经为我解决了这个问题。

【讨论】:

此外,如果您的源列是文本流,您实际上需要使用脚本组件将文本流的字节转换为临时字符串变量,进行替换,然后将字符串转换回字节用于文本流输出。这个网站有帮助:mscrmtech.com/…【参考方案3】:

名字和姓氏通常在同一个字段中并采用格式(姓氏、名字)。如果您正在使用任务->从数据库中导出数据(而不是通过您有更多选项的 SSIS)并且您需要以逗号分隔文件的形式导出到 CSV,则这需要是文本限定的。

这将有助于您选择需要双引号的非空字段...

CASE WHEN NOT PersonName IS NULL AND LEN(PersonName) > 0 THEN QUOTENAME(PersonName, '"') ELSE NULL END as 'PersonName'

结果:

人名

“柯林斯,扎克瑞 E”

【讨论】:

以上是关于将数据导出到 CSV 平面文件时如何解决嵌入的文本限定符问题?的主要内容,如果未能解决你的问题,请参考以下文章

php 导入/导出 csv 文件

如何把SQLServer表数据导出CSV文件

将数据从平面文件加载到 Sql Server 表,并使用 SSIS 导出到 excel

问题将数值数据导出到平面文件,SSIS

MS-Access 平面文件导出错误

如何将MYSQL中数据导出到EXCEL表中 python 脚本?