sql >> Database teknologi >  >> RDS >> Mysql

MySQL Ulovlig blanding af kollationer

Det er nyttigt at forstå følgende definitioner:

  • En tegnkodning detaljer, hvordan hvert symbol er repræsenteret i binært (og derfor gemt i computeren). For eksempel symbolet é (U+00E9, latin lille bogstav E med akut) er kodet som 0xc3a9 i UTF-8 (som MySQL kalder utf8 ) og 0xe9 i Windows-1252 (som MySQL kalder latin1 ).

  • Et tegnsæt er alfabetet af symboler, der kan repræsenteres ved hjælp af en given tegnkodning. Forvirrende nok bruges udtrykket også til at betyde det samme som tegnkodning.

  • En sammenstilling er en bestilling på et tegnsæt, så strenge kan sammenlignes. For eksempel:MySQL's latin1_swedish_ci sortering behandler de fleste accentvariationer af et tegn som ækvivalente med basiskarakteren, hvorimod dens latin1_general_ci sortering vil sortere dem før det næste grundtegn, men ikke ækvivalente (der er også andre, mere væsentlige forskelle:såsom rækkefølgen af ​​tegn som å , ä , ö og ß ).

MySQL bestemmer, hvilken sortering der skal anvendes på et givet udtryk som dokumenteret under Samling af udtryk :Især har sammenstillingen af ​​en kolonne forrang frem for en streng-literal.

WHERE klausulen i din forespørgsel sammenligner følgende strenge:

  1. en værdi i fos_user.username , kodet i kolonnens tegnsæt (Windows-1252) og udtrykker en præference for dens sortering latin1_swedish_ci (med en tvangsværdi på 2); med

  2. strengen literal 'Nrv⧧Kasi' , kodet i forbindelsens tegnsæt (UTF-8, som konfigureret af Doctrine) og udtrykker en præference for forbindelsens samling utf8_general_ci (med en tvangsværdi på 4).

Da den første af disse strenge har en lavere tvangsværdi end den anden, forsøger MySQL at udføre sammenligningen ved hjælp af denne strengs kollation:latin1_swedish_ci . For at gøre det forsøger MySQL at konvertere den anden streng til latin1 -men siden tegn ikke findes i det tegnsæt, mislykkes sammenligningen.

Advarsel

Man bør holde pause et øjeblik for at overveje, hvordan kolonnen i øjeblikket er kodet:du forsøger at filtrere efter poster, hvor fos_user.username er lig med en streng, der indeholder et tegn, som ikke kan findes i den kolonne !

Hvis du mener, at kolonnen gør indeholder sådanne tegn, så skrev du sandsynligvis til kolonnen, mens forbindelsestegnkodningen var sat til noget (f.eks. latin1 ), der fik MySQL til at fortolke den modtagne bytesekvens som tegn, der alle er i Windows-1252-tegnsættet.

Hvis dette er tilfældet, bør du rette dine data, før du fortsætter yderligere!

  1. konverter sådanne kolonner til den tegnkodning, der blev brugt ved dataindsættelse, hvis forskellig fra den etablerede kodning:

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
    
  2. slip kodningsinformationen forbundet med sådanne kolonner ved at konvertere dem til binary tegnsæt:

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
    
  3. tilknytte sådanne kolonner den kodning, som data faktisk blev transmitteret i, ved at konvertere dem til det relevante tegnsæt.

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

Bemærk, at hvis du konverterer fra en multi-byte-kodning, skal du muligvis øge størrelsen af ​​kolonnen (eller endda ændre dens type) for at rumme den maksimalt mulige længde af den konverterede streng.

Når man er sikker på, at kolonnerne er korrekt kodet, kan man tvinge sammenligningen til at blive udført ved hjælp af en Unicode-sortering ved enten—

  • eksplicit konvertering af værdien fos_user.username til et Unicode-tegnsæt:

    WHERE CONVERT(fos_user.username USING utf8) = ?
    
  • tvinge strengen literal til at have en lavere tvangsværdi end kolonnen (vil forårsage en implicit konvertering af kolonnens værdi til UTF-8):

    WHERE fos_user.username = ? COLLATE utf8_general_ci
    

Eller man kunne, som du siger, permanent konvertere kolonnen(erne) til en Unicode-kodning og indstille dens sortering korrekt.

Den principielle betragtning er, at Unicode-kodninger fylder mere end enkeltbyte-tegnsæt, så:

  • mere lagerplads kan være påkrævet;

  • sammenligninger kan være langsommere; og

  • indekspræfikslængder skal muligvis justeres (bemærk, at maksimum er i bytes, så det kan repræsentere færre tegn end tidligere).

Vær også opmærksom på, som dokumenteret under ALTER TABLE Syntaks :



  1. Tjek din SQLite-version

  2. ROUND(dato) Funktion i Oracle

  3. MySQL 8 ignorerer heltalslængder

  4. JSON_ARRAYAGG() Funktion i Oracle