freebcp:“Unicode数据是列的奇数字节大小。应该是偶数字节大小”

时间:2016-08-16 20:38:27

标签: sql-server unicode sql-server-2012 character-encoding freetds

此文件正常工作(UTF-8):

$ cat ok.txt
291054  Ţawī Rifā

此文件导致错误(UTF-8):

$ cat bad.txt
291054  Ţawī Rifā‘

以下是信息:

$ freebcp 'DB.dbo.table' in bad.txt ... -c
Starting copy...
Msg 20050, Level 4
Attempt to convert data stopped by syntax error in source field

Msg 4895, Level 16, State 2
Server '...', Line 1
    Unicode data is odd byte size for column 2. Should be even byte size.
Msg 20018, Level 16
General SQL Server error: Check messages from the SQL Server

唯一的区别是最后一个字符,即unicode 2018(左侧单引号)

知道是什么导致了这个错误吗?

SQL Server使用UTF-16LE(虽然TDS以UCS-2LE开头并切换到我相信)

相关列是nvarchar(200)

这是错误之前发送的数据包:

packet.c:741:Sending packet
0000 07 01 00 56 00 00 01 00-81 02 00 00 00 00 00 08 |...V.... ........|
0010 00 38 09 67 00 65 00 6f-00 6e 00 61 00 6d 00 65 |.8.g.e.o .n.a.m.e|
0020 00 69 00 64 00 00 00 00-00 09 00 e7 90 01 09 04 |.i.d.... ...ç....|
0030 d0 00 34 04 6e 00 61 00-6d 00 65 00 d1 ee 70 04 |Ð.4.n.a. m.e.Ñîp.|
0040 00 13 00 62 01 61 00 77-00 2b 01 20 00 52 00 69 |...b.a.w .+. .R.i|
0050 00 66 00 01 01 18      -                        |.f....|

3 个答案:

答案 0 :(得分:12)

更新:此问题显然已在2016-11-04发布的FreeTDS v1.00.16中修复。

我可以使用FreeTDS v1.00.15重现您的问题。它绝对看起来像U+20xx中的一个错误,当文本字段的最后一个字符具有291054 Ţawī Rifā 形式的Unicode代码点时,它会导致失败。 (感谢@srutzky纠正我对原因的结论。)正如你所说,这有效......

291054  Ţawī Rifā‘

......这失败了......

291054  Ţawī Rifā‘x

...但我发现这也有效:

x

因此,一个丑陋的解决方法是针对您的输入文件运行一个脚本,该脚本会将低位非空格Unicode字符附加到每个文本字段(例如,U+0078,即freebcp,如上例中的最后一个示例所示,使用UPDATE上传数据,然后针对导入的行运行bcp语句以去除额外的字符。

就个人而言,我倾向于从FreeTDS切换到Microsoft的Linux SQL Server ODBC驱动程序,其中包括使用此处所述的说明安装的sqlcmdlibssl.so.1.0.0实用程序: / p>

https://gallery.technet.microsoft.com/scriptcenter/SQLCMD-and-BCP-for-Ubuntu-c88a28cc

我刚刚在Xubuntu 16.04下进行了测试,虽然我不得不稍微调整一下程序来使用libssl.so.0.9.8而不是libcrypto(和bcp相同),一旦我得到了它在freebcp失败的情况下成功安装了Microsoft的connectionUrl = "jdbc:sqlserver://servername:49242" + ";databaseName=myDb" + ";integratedSecurity=false"; String myUserid = "sa", myPassword = "whatever"; String dataFileSpec = "C:/Users/Gord/Desktop/bad.txt"; try ( Connection conn = DriverManager.getConnection(connectionUrl, myUserid, myPassword); SQLServerBulkCSVFileRecord fileRecord = new SQLServerBulkCSVFileRecord(dataFileSpec, "UTF-8", "\t", false); SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(conn)) { fileRecord.addColumnMetadata(1, "col1", java.sql.Types.NVARCHAR, 50, 0); fileRecord.addColumnMetadata(2, "col2", java.sql.Types.NVARCHAR, 50, 0); bulkCopy.setDestinationTableName("dbo.freebcptest"); bulkCopy.writeToServer(fileRecord); } catch (Exception e) { e.printStackTrace(System.err); } 实用程序。

如果用于Linux的SQL Server ODBC驱动程序无法在Mac上运行,那么另一种选择是使用Microsoft JDBC驱动程序6.0 for SQL Server和一些Java代码,如下所示:

continue

答案 1 :(得分:2)

这可能是源文件的编码问题。

当您使用非标准字符时,源文件可能本身应该是unicode。其他编码使用不同的字节数(一个最多三个)来编码一个单个字符。例如。您的Unicode 20180xE2 0x80 0x98,为UTF-8。

您的数据包以.R.i.f....|结尾,而应该有ā‘。错误显示Server '...', Line 1

尝试找出源文件的编码(同时查看big and little endian)并尝试将文件转换为可靠的unicode格式。

答案 2 :(得分:1)

这可以解决它:

inf your /etc/freetds/freetds.conf

添加:

client charset = UTF-8

还发现this标志使用utf-16

  

use utf-16而不是在数据库范围内使用UCS-2   字符编码使用UTF-16。较新的Windows版本使用此功能   编码而不是UCS-2。如果客户端,这可能会导致一些问题   假设一个字符总是2个字节。