将数据导出到 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) Files
。 RFC 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 2000 和 SQL 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
关闭。
MSDN 博客上的以下帖子指出,SQL Server 2012 中已对 Flat file source supports embedded qualifiers and a variable number of columns per row
进行了更改
MSDN 博客上的另一篇文章在Embedded Qualifiers
部分下也有相同的说明。
我知道的###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 平面文件时如何解决嵌入的文本限定符问题?的主要内容,如果未能解决你的问题,请参考以下文章