程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> 一條詭異的insert語句,insert語句

一條詭異的insert語句,insert語句

編輯:MySQL綜合教程

一條詭異的insert語句,insert語句


問題背景

有同事反饋在mysql上面執行一條普通的insert語句,結果報錯,
execute failed due to >>> Incorrect string value: '\xA1;offl...' for column 'biz_info' at row 1

經過半天的折騰,終於搞清楚了來龍去脈,這裡簡單給大家分享下。為了方便說明,我將測試例子中的表和語句簡化,但不影響問題重現。

問題復現

連接字符集:UTF8

表結構:

CREATE TABLE `ggg` (
  `id` int(11) DEFAULT NULL,
  `c` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk;

root@test 06:13:48>insert into ggg values(1,concat('cardName:校園網',char(59),'offlineCardType:campus'));

Query OK, 1 row affected, 1 warning (2.51 sec)

root@test 06:14:36>show warnings\G

*************************** 1. row ***************************

Level: Warning

Code: 1366

Message: Incorrect string value: '\x91;offl...' for column 'c' at row 1 

查看結果

root@test 06:16:06>select * from ggg where id=1;

*************************** 1. row ***************************

id: 1

c: cardName:鏍″洯缃

問題分析  

      從報錯的結果來看,感覺是字符集轉換引起的問題,而且由於連接串的字符集是UTF8,表的字符集是GBK,更容易引起懷疑。但是,即使是字符集轉換,也不應該導致插入報錯,因為語句中的中文字符“校園網"都是普通漢字,UTF8->GBK不應該存在問題。那我們在回過頭來看看insert語句,唯一特殊的是使用了concat和char兩個函數。會不會跟這兩個函數有關系?char(59)實際是字符“;”,為了驗證想法,做了兩個實驗:

insert into ggg values(1,concat('cardName:校園網',';','offlineCardType:campus'));

     2.設置連接串字符集為GBK

insert into ggg values(1,concat('cardName:校園網',char(59),'offlineCardType:campus'));

果然,兩種情況執行結果都是OK的,查詢結果如下:

root@test 09:22:32>select * from ggg\G

*************************** 1. row ***************************

id: 1
c: cardName:鏍″洯缃

*************************** 2. row ***************************

id: 1
c: cardName:校園網;offlineCardType:campus

*************************** 3. row ***************************

id: 1
c: cardName:校園網;offlineCardType:campus

      跟蹤了下源代碼,找到了原因。char()函數返回的是一個binary類型字符串,在進行concat時,會導致'cardName:校園網'字符串到binary的轉換。轉換前,mysql將字符串‘cardName:校園網’看作是9個英文字符和3個漢字字符;轉換後,mysql將其看作是18個字節的二進制串,其中,UTF8字符集的三個漢字“校園網”占了9個字節。由於目標表字符集是GBK,因此在入庫時,還會發生一次binary到GBK的轉碼,“校園網”的二級制編碼是E6A0A1 E59BAD E58DA1,在轉碼過程中,由於GBK字符集只包含一個字節(編碼值<128)和二個字節的字符(漢字和特殊字符),“校園網”的二進制串會按照兩個字節拆分E6A0 A1E5 9BAD E58D A1,前面四個變為“鏍″洯缃”,解析到A1時,由於A1既不是單字節字符,又不能與後面的字節組成一個合法的GBK字符,導致轉換出錯。
      現在就很好解釋為啥改變語句後,兩種情況都OK了。第一種情況,將char(59)直接替換成‘;’,由於不涉及UF8到binary的轉換,只有utf8到gbk轉碼的過程,這個轉換是OK的,不會出現亂碼;第二種情況,將連接串的字符集設置為GBK,那麼會涉及GBK到binary的轉換,然後再從binary轉換到GBK,由於整個轉換過程並沒有二進制數據丟失,所以也是OK的。

問題產生的兩個關鍵點

解決辦法

1.char函數提供了using語法來實現返回特定字符集的字符串,比如:char(59 using utf8)

2.保證連接字符集與表字符集一致。



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved