Mysql

MySQL 默默地用文字問號替換 UTF 字元

  • March 26, 2015

我遇到了類似於這個 SO question的情況,即我正在使用一個在 latin1 表中包含 UTF8 內容的舊數據庫(我知道這很醜)。

現在我從一個完全是 utf8 並與它的數據庫一起工作的新應用程序中獲取新數據。為了支持其他遺留系統,應用程序還將其 utf8 數據的副本寫入遺留表中。據我所知,只要您將其讀回並將這些數據顯示為 UTF8,就應該可以在 latin1 表中寫入 utf8 內容。有很多教程解釋瞭如何長期解決這種情況,但除非絕對必要,否則我寧願不應用它們(遺留系統將很快被解僱,如果可能的話,我不希望有停機時間來解決這個問題)

這是一個重現我的問題的最小 SQL 腳本:

CREATE TABLE `articles` (
 `content` mediumtext NOT NULL,
 FULLTEXT KEY `content` (`content`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

SET NAMES utf8;
SET CHARACTER SET utf8;
-- Turkish word for Croatia, second char is \xC4\xB1
INSERT INTO `articles` (`content`) VALUES ('Hırvatistan');

在我的系統中,我沒有從 MySQL 得到任何錯誤,但是在INSERT語句之後,單詞的第二個字元被靜默刪除並替換為文字?(’\x3F’)。

mysql> SELECT content, HEX(content), HEX('Hırvatistan') FROM articles;
+-------------+------------------------+--------------------------+
| content     | HEX(content)           | HEX('Hırvatistan')       |
+-------------+------------------------+--------------------------+
| H?rvatistan | 483F72766174697374616E | 48C4B172766174697374616E |
+-------------+------------------------+--------------------------+

但是,如果我在http://sqlfiddle.com/上粘貼相同的腳本,當我按下“建構模式”時會出現錯誤,其中指出:

Incorrect string value: '\xC4\xB1rvat...' for column 'content' at row 1

為什麼在我的系統上,無效的 utf8 字元被簡單地刪除而我沒有收到錯誤?是否有任何 mysql 配置值可以啟用以避免這種情況?

在我目前的 latin1(帶有 utf8 內容)表中允許任何類型的字元的最簡單方法是什麼?我有很多內容,我更願意避免轉儲內容並使用其他字元集重新導入等解決方案

我做了一些嘗試來研究這個問題,這裡是結果。

當您設置連接字元集(即SET NAMES utf8)時,MySQL 透明地為您處理編碼轉換。例如,如果我à (\xE0 in latin1 \xC3A0 in utf8)使用 UTF8 連接在 latin1 表中插入 a ,它會讀取 UTF 8 值並將其儲存在表中\xE0

mysql> SELECT HEX('à');
+-----------+
| HEX('à')  |
+-----------+
| C3A0      |
+-----------+

mysql> INSERT INTO articles VALUES(50001, 'à');
Query OK, 1 row affected (0,00 sec)

mysql> SELECT content, HEX(content) FROM articles WHERE id_p = 50001;
+---------+--------------+
| content | HEX(content) |
+---------+--------------+
| à       | E0           |
+---------+--------------+
1 row in set (0,00 sec)

當我將無效的 utf8 字元插入 latin1 時,它會用問號替換它們,如我在原始問題中所示。

為了解決我的問題,我必須在原始表上執行這個命令(實際上我在它的一個小副本上嘗試過)。它負責更改字元集、排序規則以及轉換現有數據。我用 latin1 和 utf8 編碼不同的字元記錄了

mysql> select HEX(BINARY SUBSTRING(content, 17, 1)), SUBSTRING(content, 17, 1) from articles where id_p = 40\G
*************************** 1. row ***************************
HEX(BINARY SUBSTRING(content, 17, 1)): 93
           SUBSTRING(content, 17, 1): “
1 row in set (0,00 sec)

mysql> ALTER TABLE `articles` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Query OK, 34905 rows affected (1 min 10,73 sec)
Records: 34905  Duplicates: 0  Warnings: 0

mysql> select HEX(BINARY SUBSTRING(content, 17, 3)), SUBSTRING(content, 17, 3) from articles where id_p = 40\G
*************************** 1. row ***************************
HEX(BINARY SUBSTRING(content, 17, 1)): E2809C
           SUBSTRING(content, 17, 1): “
1 row in set (0,00 sec)

轉換後,char 被其內容中的 utf8 編碼替換,所有數據仍然可讀。轉換還將content列類型從更改MEDIUMTEXT為 ,LONGTEXt因為 latin1 每個字元使用 1 個字節,而 utf8 每個字元最多使用 3 個字節以避免數據截斷。

現在我正在嘗試將無效的 utf8 字元插入轉換後的表中,我得到了不同的結果。似乎無效(或不支持 4 字節)的 utf 字元只是從儲存值中刪除並帶有警告(僅在啟用警告時顯示)

$ mysql --show-warnings

mysql> INSERT INTO articles VALUES(90000, 0xC328);
Query OK, 1 row affected, 1 warning (0,00 sec)

Warning (Code 1366): Incorrect string value: '\xC3(' for column 'content' at row 1
mysql> SELECT 0xf09f8eb6;
+------------+
| 0xf09f8eb6 |
+------------+
| 🎶           |
+------------+
1 row in set (0,00 sec)

mysql> INSERT INTO articles VALUES(90001, 0xf09f8eb6);
Query OK, 1 row affected, 1 warning (0,00 sec)

Warning (Code 1366): Incorrect string value: '\xF0\x9F\x8E\xB6' for column 'content' at row 1

在此之後,我發現在我的原始範例中,如果啟用它們,也會顯示警告:

-- With warnings enabled
mysql> INSERT INTO `articles` VALUES (50000, 'Hırvatistan');
Query OK, 1 row affected, 1 warning (0,00 sec)

Warning (Code 1366): Incorrect string value: '\xC4\xB1rvat...' for column 'content' at row 1

最後,要觸發錯誤而不僅僅是警告(以避免數據失去),只需更改會話的SQL 模式或全域(在伺服器級別)

mysql> SET SESSION sql_mode = 'TRADITIONAL';
Query OK, 0 rows affected (0,00 sec)

mysql> INSERT INTO `articles` VALUES (50000, 'Hırvatistan');
ERROR 1366 (HY000): Incorrect string value: '\xC4\xB1rvat...' for column 'content' at row 1

引用自:https://dba.stackexchange.com/questions/96265