Pomocne jest zrozumienie następujących definicji:
-
kodowanie znaków szczegółowo opisuje, w jaki sposób każdy symbol jest reprezentowany w postaci binarnej (a zatem przechowywany w komputerze). Na przykład symbol
é
(U+00E9, łacińska mała litera E z ostrym) jest zakodowany jako0xc3a9
w UTF-8 (które MySQL wywołujeutf8
) i0xe9
w Windows-1252 (które MySQL wywołujelatin1
). -
Zestaw znaków to alfabet symboli, które mogą być reprezentowane przy użyciu danego kodowania znaków. Myląco, termin ten jest również używany w znaczeniu tego samego, co kodowanie znaków.
-
zestawianie jest porządkiem w zestawie znaków, dzięki czemu łańcuchy mogą być porównywane. Na przykład:
latin1_swedish_ci
sortowanie traktuje najbardziej akcentowane odmiany znaku jako równoważne znakowi bazowemu, podczas gdy jegolatin1_general_ci
sortowanie uporządkuje je przed następnym znakiem bazowym, ale nie jest równoważne (są też inne, bardziej znaczące różnice:takie jak kolejność znaków, jakå
,ä
,ö
iß
).
MySQL zdecyduje, które sortowanie należy zastosować do danego wyrażenia, zgodnie z dokumentacją w Zestawienie wyrażeń :w szczególności zestawienie kolumny ma pierwszeństwo przed zestawieniem literału ciągu.
WHERE
klauzula zapytania porównuje następujące ciągi:
-
wartość w
fos_user.username
, zakodowane w zestawie znaków kolumny (Windows-1252) i wyrażające preferencje dotyczące jej sortowanialatin1_swedish_ci
(o wartości koercji 2); z -
literał ciągu
'Nrv⧧Kasi'
, zakodowane w zestawie znaków połączenia (UTF-8, zgodnie z konfiguracją Doctrine) i wyrażające preferencje dotyczące sortowania połączeniautf8_general_ci
(o wartości koercyjności 4).
Ponieważ pierwszy z tych ciągów ma niższą wartość koercyjności niż drugi, MySQL próbuje przeprowadzić porównanie przy użyciu sortowania tego ciągu:latin1_swedish_ci
. Aby to zrobić, MySQL próbuje przekonwertować drugi ciąg na latin1
—ale ponieważ ⧧
znak nie istnieje w tym zestawie znaków, porównanie nie powiedzie się.
Ostrzeżenie
Należy zatrzymać się na chwilę, aby zastanowić się, jak kolumna jest obecnie zakodowana:próbujesz odfiltrować rekordy, w których fos_user.username
jest równy łańcuchowi, który zawiera znak, którego nie może istnieją w tej kolumnie !
Jeśli uważasz, że kolumna tak zawierają takie znaki, to prawdopodobnie napisałeś do kolumny, podczas gdy kodowanie znaków połączenia zostało ustawione na coś (np. latin1
), które spowodowały, że MySQL zinterpretował odebraną sekwencję bajtów jako znaki, które znajdują się w zestawie znaków Windows-1252.
W takim przypadku, zanim przejdziesz dalej, popraw swoje dane!
-
przekonwertuj takie kolumny na kodowanie znaków, które było używane podczas wstawiania danych, jeśli jest inne niż dotychczasowe kodowanie:
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
-
usuń informacje o kodowaniu związane z takimi kolumnami, konwertując je na
binary
zestaw znaków:ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
-
powiązać z takimi kolumnami kodowanie, w którym dane zostały faktycznie przesłane, konwertując je na odpowiedni zestaw znaków.
ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar;
Zwróć uwagę, że w przypadku konwersji z kodowania wielobajtowego może być konieczne zwiększenie rozmiaru kolumny (lub nawet zmiana jej typu), aby pomieścić maksymalną możliwą długość konwertowanego ciągu.
Gdy już mamy pewność, że kolumny są poprawnie zakodowane, można wymusić przeprowadzenie porównania przy użyciu sortowania Unicode przez...
-
jawna konwersja wartości
fos_user.username
do zestawu znaków Unicode:WHERE CONVERT(fos_user.username USING utf8) = ?
-
wymuszenie na literale ciągu znaków mniejszej wartości koercyjności niż kolumna (spowoduje niejawną konwersję wartości kolumny na UTF-8):
WHERE fos_user.username = ? COLLATE utf8_general_ci
Można też, jak mówisz, na stałe przekonwertować kolumny na kodowanie Unicode i odpowiednio ustawić ich sortowanie.
Należy wziąć pod uwagę, że kodowanie Unicode zajmuje więcej miejsca niż jednobajtowe zestawy znaków, więc:
-
może być wymagane więcej miejsca;
-
porównania mogą być wolniejsze; i
-
może zaistnieć potrzeba dostosowania długości prefiksu indeksu (zwróć uwagę, że maksymalna liczba jest w bajtach, więc może reprezentować mniej znaków niż poprzednio).
Należy również pamiętać, że zgodnie z dokumentacją w sekcji ALTER TABLE
Składnia
: