0

我正在尝试构建一个将 Apple EPF 提要数据导入 SQL Server 2012 实例的应用程序。数据以平面文件的形式提供,这些文件使用 CHAR(1) 字段分隔符和 CHAR(2) 加上换行符(char (10)) 作为行分隔符。因此,简化的 3 字段行如下所示:

field1value[char(1)]field2value[char(1)]field3value[char(2)][linefeed]
field1value[char(1)]field2value[char(1)]field3value[char(2)][linefeed]

这些文件以 UTF-8 编码并包含许多不同的语言,重要的是要保留所有数据,因此不能选择向下编码为 ASCII。但是,SQL Server 不支持 UTF-8 的 BULK INSERT。

我将其编写为 C# 命令行应用程序,并且我正在使用以下方法预处理每个文件,该方法去除文件末尾的注释行和最后的换行符,并将文件从 UTF-8转为 UTF-16:

    public void PrepareAppleEPFFile(string dataFilePath, string cleanedFilePath)
{
    if (File.Exists(cleanedFilePath))
        { return; } // Skip processing if file already exists

    using (StreamReader reader = new StreamReader(dataFilePath, Encoding.UTF8))
    {
        using (StreamWriter writer = new StreamWriter(cleanedFilePath, false, Encoding.Unicode))
        {
            string line;
            bool firstLine = true;
            while (!reader.EndOfStream)
            {
                line = reader.ReadLine();
                if (line.Length > 0 && line.Substring(0, 1) != "#") // Skip empty and commented lines
                {
                    // Done this way to avoid adding a trailing newline, which breaks BULK INSERT
                    if (!firstLine)
                    {
                        writer.Write("\n");     
                    }
                    writer.Write(line);
                    firstLine = false;
                }
            }
        }
    }
}

准备好文件后,我使用以下 BULK INSERT 语句:

BULK INSERT dbo.Application
FROM 'C:\iTunes\data\application.cleaned'
WITH ( 
    TABLOCK,
    DATAFILETYPE =  'widechar',
    ERRORFILE = 'C:\iTunes\Logs\application.log',
    FORMATFILE = 'C:\iTunes\FormatDefinitions\Application.xml'
    )

它使用以下 XML 格式文件:

<?xml version="1.0"?>
<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="\x01\x00"/>
  <FIELD ID="2" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="3" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="4" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="5" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="6" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="7" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="8" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="9" xsi:type="CharTerm"  TERMINATOR="\x01\x00"/>
  <FIELD ID="10" xsi:type="CharTerm" TERMINATOR="\x01\x00"/>
  <FIELD ID="11" xsi:type="CharTerm" TERMINATOR="\x01\x00"/>
  <FIELD ID="12" xsi:type="CharTerm" TERMINATOR="\x01\x00"/>
  <FIELD ID="13" xsi:type="CharTerm" TERMINATOR="\x01\x00"/>
  <FIELD ID="14" xsi:type="CharTerm" TERMINATOR="\x01\x00"/>
  <FIELD ID="15" xsi:type="CharTerm" TERMINATOR="\x01\x00"/>
  <FIELD ID="16" xsi:type="CharTerm" TERMINATOR="\x01\x00"/>
  <FIELD ID="17" xsi:type="CharTerm" TERMINATOR="\x02"/>
 </RECORD>
 <ROW>
  <COLUMN SOURCE="2" NAME="application_id" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="3" NAME="title" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="4" NAME="recommended_age" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="5" NAME="artist_name" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="6" NAME="seller_name" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="7" NAME="company_url" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="8" NAME="support_url" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="9" NAME="view_url" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="10" NAME="artwork_url_large" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="11" NAME="artwork_url_small" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="12" NAME="itunes_release_date" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="13" NAME="copyright" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="14" NAME="description" xsi:type="SQLNTEXT"/>
  <COLUMN SOURCE="15" NAME="version" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="16" NAME="itunes_version" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="17" NAME="download_size" xsi:type="SQLNVARCHAR"/>
  <COLUMN SOURCE="1" NAME="export_date" xsi:type="SQLNVARCHAR"/>
 </ROW>
</BCPFORMAT>

上面的方案似乎几乎可以工作,但是插入到表中的数据(使用所有 NVARCHAR/NTEXT 列)在每个字符后面都返回了 CHAR(0)。因此,如果列中的值应该是“Bob”,我实际得到的是“B[char(0)]o[char(0)]b[char(0)]”,其中 [char(0)] course 实际上显示为非打印字符显示的标准空方格。

此图像显示了数据最终外观的示例

以下查询进一步说明了该问题:

SELECT TOP 1
DATALENGTH(title) AS DATALENGTH_of_title,
DATALENGTH(cast(title as varchar(1000))) AS DATALENGTH_of_VARCHAR_CAST_Title
FROM dbo.Application

对于具有 7 个字符标题的行,上述查询为 DATALENGTH_of_title 返回值 28,为 DATALENGTH_of_VARCHAR_CAST_Title 返回值 14。这些值是应有的两倍。BULK INSERT 似乎试图重新编码已经 unicode 的数据,从而导致双编码数据,其中每个字符后跟 3 个 CHAR(0)s 而不是一个。

另一个不太严重的问题是,以 UTF-16 格式写入数据文件会导致将 BOM 添加到文件的开头,并且该 BOM 序列最终会作为第一行中第一个字段值的一部分插入文件。

我已经尝试为 BULK INSERT 命令的 CODEPAGE 参数使用各种值,但没有一个能改善这种情况,而且确实有效果的值会导致批量插入失败。例如,使用 CODEPAGE = 1200(这是 UTF-16 的代码页)会导致以下错误:

Msg 4864, Level 16, State 1, Line 1
Bulk load data conversion error (type mismatch or invalid character for the 
specified codepage) for row 1, column 1 (export_date).
4

1 回答 1

1

在查看 XML 格式文件的文档时,我终于发现了问题:

http://msdn.microsoft.com/en-us/library/ms187833.aspx

问题是我在元素中使用了xsi:type="CharTerm"而我应该使用xsi:type="NCharTerm"

于 2013-05-15T18:18:53.967 回答