同事反饋一條簡單的 INSERT 報錯:
Incorrect string value: '\xA1;offl...' for column 'biz_info' at row 1
經過半天排查,根因竟是 CHAR() 函數引發的 二進制字串 → 字符集轉換鏈路錯位。本文完整復現、分析、給出兩種修復方案。
環境與復現
-- 連線字符集:UTF8
-- 表字符集:GBK
CREATE TABLE ggg (
id INT,
c VARCHAR(100)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
-- 觸發報錯
INSERT INTO ggg VALUES (
1,
CONCAT('cardName:校園網', CHAR(59), 'offlineCardType:campus')
);
CHAR(59) 返回 ; (分號),但類型是 binary 字串。
錯誤鏈路深度解析
CONCAT 參數中混入 binary 類型 → 整個結果被隱性轉為 binary。
- 原 UTF8 字串
'cardName:校園網' (9 字節) 被視為 18 字節二進制資料。
- 寫入 GBK 表時,MySQL 嘗試
binary → GBK 轉換:
UTF8 編碼 E6 A0 A1 E5 9B AD E5 8D A1 按 GBK 雙字節切分 → E6A0 A1E5 9BAD E58D A1 → 前 4 字節成亂碼 鏍′洯缃,遇 A1 無法組成合法 GBK 字符 → 報錯。
為何兩種修改就正常?
修改 原因
CHAR(59) → ';'字面量 ';' 類型為連線字符集 (UTF8),CONCAT 全程 UTF8 → GBK 直接轉換,正常。
連線字符集改 GBK CHAR(59) 返回 GBK binary → CONCAT 結果 GBK binary → 寫入 GBK 表無轉換,正常。
兩種修復方案 (擇一)
-- 方案 1:指定 CHAR() 返回字符集 (MySQL 5.7+)
INSERT INTO ggg VALUES (
1,
CONCAT('cardName:校園網', CHAR(59 USING utf8mb4), 'offlineCardType:campus')
);
-- 方案 2:統一連線與表字符集 (推薦,治本)
-- 應用層連線字符集 = 表字符集 = utf8mb4
-- 或在 my.cnf 強制
[client]
default-character-set=utf8mb4
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
避坑清單
- 慎用
CHAR()、HEX()、UNHEX() 等返回 binary 的函數參與字串拼接。
- 統一字符集鏈路:應用連線、表、欄、資料庫全統一
utf8mb4,杜絕隱性轉換。
- 參數化查詢:用
? 佔位符傳參,驅動程式自動處理編碼,避免手工拼接 SQL。
- 開啟嚴格模式:
sql_mode=STRICT_TRANS_TABLES,讓截斷/轉換錯誤直接報錯而非靜默截斷。
結語:字符集問題 90% 來自「連線 ≠ 表」或「函數返回類型不預期」。建立「全鏈路 utf8mb4」標準,配合參數化查詢與嚴格模式,可徹底杜絕此類詭異 Bug。