Gdy klient MySQL wchodzi w interakcję z serwerem:
-
serwer odbiera dowolny tekst jedynie jako ciąg bajtów; klient wcześniej powiedział mu, jak taki tekst będzie zakodowany.
-
jeśli serwer musi następnie przechowywać ten tekst w tabeli, musi go transkodować do kodowania odpowiedniej kolumny (jeśli jest inna).
-
jeśli klient chce później pobrać taki tekst, serwer musi go transkodować do kodowania oczekiwanego przez klienta.
Jeśli kodowania używane przez klienta w krokach 1 i 3 są takie same (co zwykle ma miejsce, zwłaszcza gdy klient w obu przypadkach jest tą samą aplikacją), często pozostaje niezauważone, jeśli klient używa kodowania innego niż to, o którym mówi. Załóżmy na przykład, że klient mówi MySQL, że użyje latin1
, ale faktycznie wysyła dane w utf8
:
-
Ciąg
'Jazz–Man'
jest wysyłany na serwer w UTF-8 jako0x4a617a7ae280934d616e
. -
MySQL, dekodując te bajty w Windows-1252, rozumie, że reprezentują one ciąg
'Jazz–Man'
. -
Aby przechowywać w
utf8
kolumna, MySQL transkoduje ciąg do jego kodowania UTF-80x4a617a7ac3a2e282ace2809c4d616e
. Można to zweryfikować za pomocąSELECT HEX(name) FROM lessons WHERE id=79510
. -
Kiedy klient pobiera wartość, MySQL myśli, że chce ją w
latin1
a więc transkoduje do kodowania Windows-12520x4a617a7ae280934d616e
. -
Gdy klient otrzymuje te bajty, dekoduje je jako UTF-8 i dlatego rozumie, że ciąg to
'Jazz–Man'
.
Wniosek :klient nie zdaje sobie sprawy, że coś jest nie tak. Problemy są wykrywane tylko wtedy, gdy inny klient (taki, który nie podaje błędnie swojego połączenia UTF-8 jako latin1
) próbuje użyć tabeli. W Twoim przypadku miało to miejsce, gdy mysqldump uzyskał eksport danych; używając --default-character-set=latin1 --skip-set-charset
opcje skutecznie zmusiły mysqldump do zachowywania się w ten sam sposób, co Twoja aplikacja, więc w efekcie uzyskano poprawnie zakodowane dane.
Aby rozwiązać problem w przyszłości, musisz:
-
Skonfiguruj swoją aplikację tak, aby poprawnie ustawiała zestaw znaków połączenia MySQL (np. ustaw
encoding: utf8
wconfig/database.yml
dla szyn); -
Przekoduj dane w Twojej bazie, np.
UPDATE lessons SET name = BINARY CONVERT(name USING latin1)
(zauważ, że należy to zrobić dla każdej błędnie zakodowanej kolumny tekstu).
Pamiętaj też, że prawdopodobnie będziesz chciał wykonać te dwie czynności atomowo, co może wymagać trochę przemyślenia.