C# 将Apple EPF Unicode数据批量插入SQL Server 2012
我正在尝试构建一个应用程序,将Apple EPF提要数据导入SQL Server 2012实例。数据以平面文件的形式提供,平面文件使用CHAR(1)字段分隔符和CHAR(2)加上换行符(CHAR(10))作为行分隔符。因此,简化的3字段行如下所示:C# 将Apple EPF Unicode数据批量插入SQL Server 2012,c#,sql-server,unicode,bulkinsert,C#,Sql Server,Unicode,Bulkinsert,我正在尝试构建一个应用程序,将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[ch
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的大容量插入
我将此作为一个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 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>
对于标题为7个字符的行,上面的查询返回值28表示标题的DATALENGTH_,返回值14表示标题的DATALENGTH_。这些值是其应有值的两倍。大容量插入似乎试图对已经存在的unicode数据进行重新编码,从而生成双编码数据,其中每个字符后面都有3个字符(0),而不是一个字符
另一个不太严重的问题是,在UTF-16中写入数据文件会导致将BOM添加到文件的开头,而BOM序列最终会作为文件第一行中第一个字段值的一部分插入
我尝试过为bulkinsert命令的CODEPAGE参数使用不同的值,但是没有一个值能够改善这种情况,而那些确实起作用的值导致了bulkinsert失败。例如,使用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).
在查看XML格式文件的文档时,我终于找到了问题所在: 问题是我在元素中使用了xsi:type=“CharTerm”,而我本应该使用xsi:type=“NCharTerm”
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).