簡體   English   中英

MySQL非法混合排序規則

[英]MySQL Illegal mix of collations

查看我的prod日志后,我提到了一些錯誤:

[2012-08-31 15:56:43] request.CRITICAL: Doctrine\DBAL\DBALException: 
An exception occurred while executing 'SELECT t0.username ....... FROM fos_user t0 WHERE t0.username = ?'
with params {"1":"Nrv\u29e7Kasi"}:

SQLSTATE[HY000]: General error: 1267 Illegal mix of collations (latin1_swedish_ci,IMPLICIT)
and (utf8_general_ci,COERCIBLE) for operation '=' 

Alghout我在學說cfg下有UTF-8默認值:

doctrine:
    dbal:
        charset:  UTF8

看來我所有的MySQL表都在latin1_swedish_ci ,所以我的問題是:

我是否可以手動將排序規則更改為utf8_general_ci ,無需任何復雜/預防措施?

理解以下定義很有幫助:

  • 字符編碼詳細說明了每個符號如何以二進制表示(因此存儲在計算機中)。 例如,符號é (U + 00E9,拉丁小字母E急性)被編碼0xc3a9UTF-8 (其MySQL調用utf8 )和0xe9視窗-1252 (其MySQL調用latin1 )。

  • 字符集是可以使用給定字符編碼表示的符號字母表。 令人困惑的是,該術語也用於表示與字符編碼相同的含義。

  • 排序規則是字符集的排序,因此可以比較字符串。 例如:MySQL的latin1_swedish_ci collat​​ion將字符的大多數重音變體視為等同於基本字符,而其latin1_general_ci排序將在下一個基本字符之前對它們進行排序,但不等同(還有其他更重要的差異:例如順序像åäöß這樣的人物。

MySQL將決定應該對給定表達式應用哪種排序規則,如表達式排序規則所述 :特別是,列的排序優先於字符串文字的排序規則

查詢的WHERE子句比較以下字符串:

  1. 在一個值fos_user.username ,在列的字符集(視窗-1252)並表示其核對的優先編碼latin1_swedish_ci (為2的可壓縮性的值);

  2. 字符串文字'Nrv⧧Kasi' ,在連接的字符集(UTF-8,由Doctrine配置)中編碼,並表示對連接的校對utf8_general_ci (強制值為4)的首選項。

由於這些字符串中的第一個具有比第二個字符串更低的強制性值,因此MySQL嘗試使用該字符串的校對來執行比較: latin1_swedish_ci 為此,MySQL嘗試將第二個字符串轉換為latin1但由於該字符集中不存在字符,因此比較失敗。


警告

我們應暫停一下,考慮當前列的編碼方式:您正在嘗試過濾fos_user.username等於包含該列中存在的字符的字符串的記錄!

如果您認為該列確實包含此類字符,那么您可能會在連接字符編碼設置為某些內容(例如latin1 )時寫入該列,導致MySQL將接收到的字節序列解釋為Windows-1252中的所有字符字符集。

如果是這種情況,在繼續之前你應該修復你的數據!

  1. 將這些列轉換為數據插入時使用的字符編碼,如果與現有編碼不同:

     ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo; 
  2. 刪除與這些列關聯的編碼信息,方法是將它們轉換為binary字符集:

     ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary; 
  3. 通過將數據轉換為相關字符集,將數據實際傳輸的編碼與這些列相關聯。

     ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar; 

請注意,如果從多字節編碼轉換,您可能需要增加列的大小(甚至更改其類型),以便適應轉換后的字符串的最大可能長度。


一旦確定列被正確編碼,就可以強制通過以下任一方式使用Unicode整理進行比較 -

  • 顯式地將值fos_user.username轉換為Unicode字符集:

     WHERE CONVERT(fos_user.username USING utf8) = ? 
  • 強制字符串文字具有比列更低的強制性值(將導致將列的值隱式轉換為UTF-8):

     WHERE fos_user.username = ? COLLATE utf8_general_ci 

或者,正如您所說,可以將列永久轉換為Unicode編碼並適當地設置其排序規則。

我是否可以手動將排序規則更改為utf8_general_ci ,無需任何復雜/預防措施?

主要考慮因素是Unicode編碼比單字節字符集占用更多空間,因此:

  • 可能需要更多存儲空間;

  • 比較可能會更慢;

  • 可能需要調整索引前綴長度(請注意,最大值以字節為單位,因此可能表示比以前更少的字符)。

另外,請注意,如ALTER TABLE語法中所述

要將表缺省字符集和所有字符列( CHARVARCHARTEXT )更改為新字符集,請使用如下語句:

  ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name ; 

對於數據類型為VARCHAR或其中一個TEXT類型的列, CONVERT TO CHARACTER SET將根據需要更改數據類型,以確保新列足夠長,以存儲與原始列一樣多的字符。 例如, TEXT列有兩個長度字節,用於存儲列中值的字節長度,最大值為65,535。 對於latin1 TEXT列,每個字符都需要一個字節,因此該列最多可以存儲65,535個字符。 如果列轉換為utf8 ,則每個字符最多可能需要三個字節,最大可能長度為3×65,535 = 196,605字節。 該長度不適合TEXT列的長度字節,因此MySQL將數據類型轉換為MEDIUMTEXT ,這是最小的字符串類型,長度字節可以記錄值196,605。 同樣, VARCHAR列可能會轉換為MEDIUMTEXT

要避免更改剛才描述的類型的數據類型,請不要使用CONVERT TO CHARACTER SET 而是使用MODIFY更改單個列。

那就對了。 我遇到了這個問題,最好的快速和快速的解決方案是

         CONVERT(fos_user.username USING utf8)

只需按命令轉換表的字符集如下,

ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM