某些计算机上的 utf8 表出现“不正确的字符串值”错误

问题描述

我正在开发与远程 MariaDB 数据库(通过 ADO 和 ODBC 驱动程序)通信的 Delphi 应用程序。发现在某些计算机上无法将 Cyrillic 字符串添加到 DB。像这样的错误信息: [MysqL][ODBC 8.0(w) Driver][MysqLd-5.5.5-10.3.29-MariaDB]Incorrect string value: '\xC2\xC2\xC8 \xF1\xE8…' for column <here's target tables and columns name> at row 1
字符集为 utf8,整理 - utf8_bin。它在我装有 Windows 10 的计算机上完美运行,但在装有 Windows 8 和 Vista 的计算机上会出错。因此,据我了解,问题不在数据库设置中,而在操作系统中。它可以是什么?也许是区域设置之类的?

更新

表格:

SHOW CREATE TABLE equip;

CREATE TABLE `equip` (
 `eqid` int(10) unsigned NOT NULL AUTO_INCREMENT,`eqname` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'Наименование оборудования',PRIMARY KEY (`eqid`),UNIQUE KEY `eqname_UNIQUE` (`eqname`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

连接到数据库

  procedure OpenDBConnection;
  begin
    dmMain.dbConnection.Connected := False;
    dmMain.dbConnection.ConnectionString := Format(
      'Driver={MysqL ODBC 8.0 Unicode Driver};Port=%s;Server=%s;Database=%s;User=%s;Password=%s;',[Port,Server,Database,User,Password]);
    dmMain.dbConnection.Connected := True;
  end;

数据库插入数据:

const
  tblEquip = 'equip';
  colEquipName = 'eqname';

  queryInsertEquipment =
    ' INSERT INTO ' + tblEquip + '(' +
    colEquipName + ') ' +
    'VALUES(:' +
    colEquipName + '); ';


function TdmMain.AddEquipment(const Equip: TEquipment;
  const RefreshEquipmentListDataSet: Boolean): Integer;
begin
  Result := 0;

  try
    cmdTemp.CommandText := queryInsertEquipment;
    cmdTemp.Parameters.ParamByName(colEquipName).Value := Equip.Name;
    cmdTemp.Execute;
    Result := GetLastID;

    if RefreshEquipmentListDataSet then
      RefreshDataSet(dsetEquipment,colEquipID,Result);
  except on E: Exception do
    fmMessage.ShowMessage(msgErrorAddEquipment,mtError);
  end;
end;

解决方法

您必须定义客户端和数据库之间的通信编码。如果不这样做,则数据库会采用一种编码,而客户端可能会采用不同的编码。默认情况下,较旧的安装很可能不使用 UTF-8。仅仅因为您的所有数据库表都是用 Unicode 字符集和排序规则定义的,并不意味着写入和读取这些表的数据以相同的编码进行通信。

  • MySQLSET NAMES 是一次设置多个变量的便捷方式。使用像 utf8 这样的编码不会转换任何东西 - 它告诉数据库解释该编码中的所有传入字节(并在为所有结果发送字节时使用该编码)。
  • PHPmysqli_set_charset() 在内部做同样的事情,但提供这个函数也是为了让你知道你对其他方便的函数(例如 mysqli_real_escape_string())的选择。立>

在您确定实际以 UTF-8 进行通信后,只剩下您的客户端本身:如果您交出 Delphi Widestring,驱动程序是否会为您进行转换?或者您是否必须提供实际的 UTF-8 编码字节作为您的查询?这些链接不是为了好玩:文档告诉你足够多的事实,可以检查和测试 - 没有必要质疑它们并做出疯狂的假设 - 只会让你理解错误。