PDO DBLIB 多字节(中文)字符编码 - SQL 服务器

PDO DBLIB multibyte (chinese) character encoding - SQL server

在 Linux 机器上,我使用 PDO DBLIB 连接到 MSSQL 数据库并在 SQL_Latin1_General_CP1_CI_AS table 中插入数据。问题是当我尝试插入中文字符(多字节)时,它们被插入为 哈市香åŠåŒºç æ±Ÿè·¯å·.

我的(部分)代码如下:

$DBH = new PDO("dblib:host=$myServer;dbname=$myDB;", $myUser, $myPass);

$query = "
    INSERT INTO UserSignUpInfo
    (FirstName)
    VALUES
    (:firstname)";

$STH = $DBH->prepare($query);

$STH->bindParam(':firstname', $firstname);

到目前为止我尝试过的:

  1. $firstname 上执行 mb_convert_encodingUTF-16LE 并在查询中 CAST 为 VARBINARY,例如:

    $firstname = mb_convert_encoding($firstname, 'UTF-16LE', 'UTF-8');

    VALUES
    (CAST(:firstname AS VARBINARY));
    

    这导致正确插入字符,直到有一些非多字节字符中断 PDO 执行。

  2. 将我的连接设置为 utf8:

    $DBH = new PDO("dblib:host=$myServer;dbname=$myDB;charset=UTF-8;", $myUser, $myPass);
    $DBH->exec('SET CHARACTER SET utf8');
    $DBH->query("SET NAMES utf8");
    
  3. 在我的 freetds.conf

    中将 client charset 设置为 UTF-8

    没有影响。

有什么办法可以在 SQL 数据库中插入多字节数据吗?还有其他解决方法吗? 我曾想过尝试 PDO ODBC 甚至 mssql,但我认为在浪费更多时间之前最好先在这里问一下。

提前致谢。

编辑:

我最终使用了 MSSQLN 数据类型前缀。当我有更多时间时,我会交换并尝试PDO_ODBC。谢谢大家的回答!

本link对汉字的解释MYSQL。 Can't insert Chinese character into MySQL。 您必须创建 table table_name () CHARACTER SET = utf8; Use UTF-8 当你插入到 table

set username utf8; INSERT INTO table_name (ABC,VAL); 

abd 在 CHARACTER SET utf8 COLLATE utf8_general_ci;

中创建数据库

然后你可以在table

中插入汉字

排序规则在这里无关紧要。

双字节字符需要存储在nvarcharncharntext字段中。您不需要执行任何转换。

n 数据类型前缀代表国家,它导致 SQL 服务器将文本存储为 Unicode (UTF-16)。

编辑:

PDO_DBLIB 不支持 Unicode,现已弃用。

如果您可以切换到 PDO_ODBC,Microsoft 为 Linux 提供免费的 SQL 服务器 ODBC 驱动程序,支持 Unicode。

Microsoft - SQL Server ODBC Driver Documentation

Blog - Installing and Using the Microsoft SQL Server ODBC Driver for Linux

只需使用 nvarchar、ntext、nChar,当你想插入时 使用

INSERT INTO UserSignUpInfo
    (FirstName)
    VALUES
    (N'firstname');

N 将引用 Unicode 字符,它是全球通用的。

参考:

https://aalamrangi.wordpress.com/2012/05/13/storing-and-retrieving-non-english-unicode-characters-hindi-czech-arabic-etc-in-sql-server/

https://technet.microsoft.com/en-us/library/ms191200(v=sql.105).aspx

https://irfansworld.wordpress.com/2011/01/25/what-is-unicode-and-non-unicode-data-formats/

您可以为 table 列使用 Unicode 兼容数据类型以支持外语(例外情况显示在 EDIT 2 中)。

(char, varchar, text) Versus (nchar, nvarchar, ntext)

非 Unicode :

最适合美国英语:“使用 1 个字节对每个字符进行编码的数据类型的一个问题是该数据类型只能表示 256 个不同的字符。这会强制为不同的字母表设置多个编码规范(或代码页)比如欧洲字母,比较小,也无法处理像日文汉字,韩文韩文等几千字的系统

Unicode

最适合需要至少支持一种外语的系统:“Unicode 规范为全球商业中广泛使用的大多数字符定义了单一编码方案。所有计算机都一致地将 Unicode 数据中的位模式转换为使用单一 Unicode 规范的字符。这确保相同的位模式始终在所有计算机上转换为相同的字符。数据可以从一个数据库或计算机自由传输到另一个数据库或计算机,而不用担心接收系统会将位模式转换为字符不正确。

示例:

我也试过一个例子,你可以在下面查看它的屏幕,这将有助于解决与外语插入相关的问题,因为问题是正确的 now.The 列,如下面 nvarchar 和它确实支持中文

编辑 1:

讨论了另一个相关问题here

编辑 2:

显示了不受 Unicode 支持的脚本 here

Is there any way at all, to insert multibyte data in [this particular] SQL database? Is there any other workaround?

  1. 如果您可以切换到 PDO_ODBC,Microsoft 为 Linux 提供免费的 SQL 服务器 ODBC 驱动程序(仅适用于 64 位 Red Hat Enterprise Linux,以及支持 Unicode 的 64 位 SUSE Linux Enterprise)。

  2. 如果能改成PDO_ODBC,那么插入Unicode的N前缀就可以了

  3. 如果您可以将受影响的 table 从 SQL_Latin1_General_CP1_CI_AS 更改为 UTF-8(这是 MSSQL 的默认设置),那么就可以了要理想。

您的情况受到更多限制。此解决方案适用于输入字符串中混合了多字节和非多字节字符的情况,并且您需要将它们保存为拉丁 table,而 N 数据类型前缀不是工作,并且您不想更改 PDO DBLIB(因为 Microsoft 的 Unicode PDO_ODBC 在 linux 上是 barely supported)。这是一种解决方法。

有条件地将输入字符串编码为base64。毕竟,这就是我们如何安全地与电子邮件一起传输图片。

工作示例:

$DBH = new PDO("dblib:host=$myServer;dbname=$myDB;", $myUser, $myPass);

$query = "
INSERT INTO [Whosebug].[dbo].[UserSignUpInfo]
           ([FirstName])
     VALUES
           (:firstname)";

$STH = $DBH->prepare($query);

$firstname = "输入中国文字!Okay!";

/* First, check if this string has any Unicode at all */
if (strlen($firstname) != strlen(utf8_decode($firstname))) {
    /* If so, change the string to base64. */
    $firstname = base64_encode($firstname);
}

$STH->bindParam(':firstname', $firstname);
$STH->execute(); 

然后向后退,您可以测试 base64 字符串,并在不损坏现有条目的情况下仅 解码它们,如下所示:

while ($row = $STH->fetch()) {
    $entry = $row[0];

    if (base64_encode(base64_decode($entry , true)) === $entry) {

         /* Decoding and re-encoding a true base64 string results in the original entry */
         print_r(base64_decode($entry) . PHP_EOL);

    } else {

         /* Previous entries not encoded will fall through gracefully */
         print_r($entry  . PHP_EOL);
    }
}

条目将这样保存:

Guan Tianlang
5pys6Kqe44KS5a2maGVsbG8=

但您可以轻松地将它们转换回:

Guan Tianlang
输入中国文字!Okay!