今天有客戶向我咨詢:數據庫由ZHS16GBK字符集修改為AL32UTF8字符集,發現中文的數據中小部分出現亂碼,客戶認為AL32UTF8明明可以支持更多的文字,不應該出現這樣的情況才對。
從現象看,基本可以確認故障是字符集轉換導致的,Oracle也強烈不建議做這種字符集轉換的操作,幸好該客戶的操作只是在一個測試環境中操作的。不過,之前也一直有個誤區,我們都知道AL32UTF8是可以支持多國語言的字符集,對於中文字節存儲占用空間比ZHS16GBK多,然后第一反應就認為AL32UTF8應該是ZHS16GBK的超集。而如果是絕對的超集,就不應該出現任何亂碼的情況,可實際用戶反饋的現象的確是有小部分出現亂碼的情況,所以有必要在測試環境再次驗證一下。
1.首先我的庫ZHS16GBK的字符集
SQL> select userenv('language') from dual;
USERENV('LANGUAGE')
----------------------------------------------------
AMERICAN_AMERICA.ZHS16GBK
2.嘗試修改字符集為AL32UTF8
直接嘗試修改,會發現Oracle明確給出錯誤提示ORA-12712:新的字符集必須是舊字符集的超集。這就說明我們要改的AL32UTF8字符集並不是ZHS16GBK的超集。
SQL> alter database character set al32utf8;
alter database character set al32utf8
*
ERROR at line 1:
ORA-12712: new character set must be a superset of old character set
如果非要修改,可以加internal_use參數強制修改,而這樣的操作,自然就很有可能會造成部分數據出現亂碼:
SQL> alter database character set internal_use al32utf8;
Database altered.
SQL> select userenv('language') from dual;
USERENV('LANGUAGE')
--------------------------------------------------------------------------------
AMERICAN_AMERICA.AL32UTF8
此時如果我們通過PL/SQL Developer工具連接到數據庫,還會有這樣的警告信息:

而客戶端是Windows,chcp結果是936,也就是ZHS16GBK,這也進一步說明了ZHS16GBK和AL32UTF8字符集的不同。
同時實驗還驗證,如果數據庫字符集本身是AL32UTF8,想修改成為ZHS16GBK字符集,也是一樣的情況,需要加internal_use參數才可以轉換,也就是說這種轉換一樣可能出現亂碼,不過這個情況反倒好理解,也符合我們之前的認知,就不再贅述了。
