Mysql
 sql >> Datenbank >  >> RDS >> Mysql

MySQL Illegale Mischung von Sortierungen

Es ist hilfreich, die folgenden Definitionen zu verstehen:

  • Eine Zeichencodierung Details, wie jedes Symbol binär dargestellt (und daher im Computer gespeichert) wird. Zum Beispiel das Symbol é (U+00E9, lateinischer Kleinbuchstabe E mit Akut) ist kodiert als 0xc3a9 in UTF-8 (was MySQL utf8 nennt ) und 0xe9 in Windows-1252 (was MySQL latin1 nennt ).

  • Ein Zeichensatz ist das Alphabet von Symbolen, die mit einer bestimmten Zeichencodierung dargestellt werden können. Verwirrenderweise wird der Begriff auch gleichbedeutend mit Zeichenkodierung verwendet.

  • Eine Sammlung ist eine Sortierung nach einem Zeichensatz, damit Zeichenketten verglichen werden können. Zum Beispiel:MySQLs latin1_swedish_ci collation behandelt die meisten akzentuierten Variationen eines Zeichens als gleichwertig mit dem Basiszeichen, während sein latin1_general_ci Die Sortierung ordnet sie vor dem nächsten Basiszeichen an, ist aber nicht äquivalent (es gibt auch andere, bedeutendere Unterschiede:wie die Reihenfolge von Zeichen wie å , ä , ö und ß ).

MySQL entscheidet, welche Sortierung auf einen bestimmten Ausdruck angewendet werden soll, wie unter dokumentiert Sortierung von Ausdrücken :Insbesondere hat die Sortierung einer Spalte Vorrang vor der eines String-Literals.

Das WHERE -Klausel Ihrer Abfrage vergleicht die folgenden Zeichenfolgen:

  1. ein Wert in fos_user.username , die im Zeichensatz der Spalte (Windows-1252) codiert ist und eine Präferenz für die Sortierung latin1_swedish_ci ausdrückt (mit einem Zwangswert von 2); mit

  2. das String-Literal 'Nrv⧧Kasi' , kodiert im Zeichensatz der Verbindung (UTF-8, wie von Doctrine konfiguriert) und drückt eine Präferenz für die Sortierung utf8_general_ci der Verbindung aus (mit einem Zwangswert von 4).

Da der erste dieser Strings einen niedrigeren Koerzitivitätswert hat als der zweite, versucht MySQL, den Vergleich mit der Sortierung dieses Strings durchzuführen:latin1_swedish_ci . Dazu versucht MySQL, den zweiten String in latin1 umzuwandeln —aber seit dem Zeichen in diesem Zeichensatz nicht existiert, schlägt der Vergleich fehl.

Warnung

Man sollte einen Moment innehalten, um zu überlegen, wie die Spalte derzeit codiert ist:Sie versuchen, nach Datensätzen mit fos_user.username zu filtern ist gleich einer Zeichenkette, die ein Zeichen enthält, das nicht darf in dieser Spalte vorhanden sind !

Wenn Sie glauben, dass die Spalte tut solche Zeichen enthalten, dann haben Sie wahrscheinlich in die Spalte geschrieben, während die Verbindungszeichencodierung auf etwas eingestellt war (z. B. latin1 ), was dazu führte, dass MySQL die empfangene Bytefolge als Zeichen interpretierte, die alle im Windows-1252-Zeichensatz enthalten sind.

Wenn dies der Fall ist, sollten Sie Ihre Daten korrigieren, bevor Sie fortfahren!

  1. Konvertieren Sie solche Spalten in die Zeichencodierung, die beim Einfügen von Daten verwendet wurde, falls sie von der bestehenden Codierung abweicht:

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
    
  2. Löschen Sie die Codierungsinformationen, die solchen Spalten zugeordnet sind, indem Sie sie in den binary konvertieren Zeichensatz:

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
    
  3. ordnen Sie solchen Spalten die Codierung zu, in der Daten tatsächlich übertragen wurden, indem Sie sie in den entsprechenden Zeichensatz konvertieren.

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

Beachten Sie, dass Sie beim Konvertieren von einer Multi-Byte-Codierung möglicherweise die Größe der Spalte erhöhen (oder sogar ihren Typ ändern) müssen, um die maximal mögliche Länge der konvertierten Zeichenfolge aufzunehmen.

Sobald man sicher ist, dass die Spalten korrekt kodiert sind, kann man den Vergleich erzwingen, indem man eine Unicode-Sortierung verwendet, indem man entweder –

  • explizites Konvertieren des Werts fos_user.username in einen Unicode-Zeichensatz:

    WHERE CONVERT(fos_user.username USING utf8) = ?
    
  • Erzwingen, dass das Zeichenfolgenliteral einen niedrigeren Koerzitivitätswert als die Spalte hat (führt zu einer impliziten Konvertierung des Spaltenwerts in UTF-8):

    WHERE fos_user.username = ? COLLATE utf8_general_ci
    

Oder man könnte, wie Sie sagen, die Spalte(n) dauerhaft in eine Unicode-Codierung konvertieren und ihre Sortierung entsprechend festlegen.

Die Hauptüberlegung ist, dass Unicode-Kodierungen mehr Platz beanspruchen als Einzelbyte-Zeichensätze, also:

  • möglicherweise ist mehr Speicherplatz erforderlich;

  • Vergleiche können langsamer sein; und

  • Index-Präfixlängen müssen möglicherweise angepasst werden (beachten Sie, dass das Maximum in Byte angegeben ist und daher möglicherweise weniger Zeichen als zuvor darstellt).

Beachten Sie auch, wie unter ALTER TABLE Syntax :