2014-02-20 6 views
6

Für eine Website mit internationaler Unterstützung verwende ich utf8mb4 charset und utf8mb4_unicode_ci Sortierung in den meisten Tabellen und Spalten. Leistung ist nicht vorrangig und genaue Sortierung in einer Vielzahl von Sprachen ist wichtig.Klärung möglicher Probleme mit Gleichheitszeichen mit nicht-binären Sortierfolgen

Ich verstehe, wie die utf8mb4_general_ci und utf8mb4_unicode_ci Sortierungen mit Vergleichen in der Regel arbeiten mit akzentuierten Zeichen, nämlich dass:

SELECT column FROM table WHERE column='abad'; 

Würde wieder sowohl ‚abad‘ und ‚Abad‘

Während der Erforschung UTF8-Unterstützung in MySQL, ich stieß auf ein vermeintliches Problem mit den nicht-binären utf8___ Kollatierungen. Die Seite um http://mzsanford.com/blog/mysql-and-unicode/ beschreibt ein Problem mit Änderungen, die nicht in einigen Updates gespeichert werden. Er sagt: 'Beim Aktualisieren eines Datensatzes erscheint MySQL (oder zumindest InnoDB) vor dem Aktualisieren eines Datensatzes auf Gleichheit. Da eine Änderung nur von Akzenten durch die Kollatierung als gleich angesehen wird, überspringt MySQL den Schreibvorgang (was I/O-Overhead spart) und gibt Erfolg zurück, da es denkt, dass es einen Schreibvorgang optimiert statt zu scheitern.

Ich interpretiere das als: Wenn Sie versucht haben, einen Datensatz nur in den Akzenten eines Feldes zu ändern, wird es nicht richtig aktualisiert (weil MySQL denkt, dass es bereits übereinstimmt). Aber ich konnte das nicht replizieren. Ich habe einen einfachen Testfall:

CREATE DATABASE test_utf8 
    CHARACTER SET utf8mb4 
    COLLATE utf8mb4_unicode_ci; 

USE test_utf8; 

CREATE TABLE test (
    id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, 
    text VARCHAR(300) NOT NULL, 
    PRIMARY KEY (id) 
) ENGINE = INNODB; 

INSERT INTO test (text) VALUES ('abád'); 

UPDATE test SET text='abad' WHERE id=1; 

Dies ist jedoch aktualisiert den Wert korrekt (trotz nur einer Änderung der Akzent auf ein Zeichen). War das nur ein Problem in einer älteren MySQL-Version? Oder tritt dieses Problem in etwas anderen Umständen auf?


Ich würde auch freuen, wenn Sie einen Moment Zeit haben einige meiner Notizen von wenigen Konzepte rund um das Thema zu lesen und sehen, ob ich irgendwelche Missverständnisse haben. Wenn es fehlerfrei ist, wird es vielleicht nützliche Informationen für jemanden sein.

Der utf8-Zeichensatz von MySQL bietet keine echte utf8-Unterstützung, da die Zeichen nur 1-3 Byte umfassen. Für echte utf8-Unterstützung werden Sie wahrscheinlich utf8mb4 verwenden wollen.

Im Allgemeinen wird utf8mb4_unicode_ci mit der sprachangepassten Sortierung genauer sein, aber es gibt einen leichten Leistungsabfall im Gegensatz zur Verwendung von utf8mb4_general_ci.

Wenn bestimmte Spalten nicht sortiert werden müssen und Vergleichs-/Gleichheitsüberprüfungen verwendet werden, sollten Sie utf8mb4_bin verwenden, da dies etwas schneller ist.

Akzentuierte Zeichen werden in den Sortierfolgen utf8mb4_general_ci und utf8mb4_unicode_ci als gleich behandelt. Aus diesem Grund ist es eine schlechte Kollationsauswahl für Spalten, die eindeutige Werte (z. B. Primärschlüssel) haben müssen. In diesem Fall sollte utf8mb4_bin verwendet werden. Und wenn ein Feld für die Eindeutigkeit akzentuiert werden muss, aber auch an einer Stelle sortiert werden muss, kann es als utf8mb4_bin gespeichert werden, und Sie können bei der Bestellung eine Sortierklausel in der Abfrage verwenden. Bsp .:

SELECT column FROM table ORDER BY column COLLATE utf8mb4_unicode_ci; 

Dies wird dazu führen, dass die Sortierung trotz ihres internen Speichers in einer Binärsortierung sprachsortiert wird. Dies wirkt sich auf die Leistung aus, da die Sortierung des Felds bestimmt, wie es indiziert wird. Der Leistungsunterschied der Abfrage wäre dem Leistungsunterschied beim Sortieren einer nicht indizierten Spalte gegenüber einer indizierten Spalte ähnlich.

Standardmäßig wird eine Suche unter den Sortierfolgen utf8mb4_unicode_ci oder utf8mb4_general_ci nicht akzentuiert, sodass eine Suche nach "abad" "abad" und "abád" zurückgibt. Wenn Sie also akzentsensitive Suchen durchführen möchten, müssen Sie entweder die Sortierfolge der Spalte auf utf8mb4_binary setzen (wenn alle Suchvorgänge akzentsensitiv sind) oder eine Sortierklausel in der Abfrage verwenden (wenn die meisten Suchvorgänge akzentblind sein sollen). Da bei der Sortierfolge utf8mb4_bin die Groß-/Kleinschreibung beachtet wird, müssen Sie die Abfrage auch ändern, wenn Sie die Groß-/Kleinschreibung beachten, jedoch nicht auf Akzente achten möchten. Zum Beispiel (unter der Annahme Suchbegriff bereits Klein in der serverseitigen Skriptsprache gemacht worden):

(Assuming the data is stored with a collation of utf8mb4_bin) 
SELECT column FROM table WHERE LOWERCASE(column) LIKE 'abád'; 

(Assuming the data is stored with a collation of utf8mb4_unicode_ci) 
SELECT column FROM table WHERE LOWERCASE(column) LIKE 'abád' COLLATE utf8mb4_bin; 

Auch aus der MySQL-Dokumentation (einschließlich es nur für andere): Wenn die Werte aus verschiedenen Spalten zu vergleichen, erklären diese Spalten mit dem gleichen Zeichensatz und der gleichen Kollatierung, wo immer dies möglich ist, um beim Ausführen der Abfrage Zeichenfolgenkonvertierungen zu vermeiden.

Antwort

1

Ich bin kein Experte, aber ich habe versucht, was Sie mit ein paar Extras tat ...

ich Ihre Setup ausgeführt und die folgenden auf MySQL 5.6.17:

SELECT COUNT(*) FROM test WHERE `text`='abad'; 
SELECT COUNT(*) FROM test WHERE `text`='abád'; 
UPDATE test SET text='abád' WHERE id=1; 

Die wählt beide Wie erwartet 1 Zeile zurückgeben, und das Update (wie Ihr Update) ändert 1 Zeile, entgegengesetzt zu dem, was der Blog vorschlägt.

Ich dachte, es könnte eine niedrigere Ebene Optimierung, aber ich bemerkte etwas interessant, wenn ich versuchte, diese wieder zum Laufen in dem Kommandozeilen-Client (statt Workbench):

mysql> SELECT COUNT(*) FROM test WHERE `text`='abád'; 
ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and 
(utf8_general_ci,COERCIBLE) for operation '=' 
mysql> UPDATE test SET text='abád' WHERE id=1; 
ERROR 1366 (HY000): Incorrect string value: '\xA0d' for column 'text' at row 1 

So lief ich das zu sehen was los war:

mysql> SELECT collation('abád'); 
+-------------------+ 
| collation('abád') | 
+-------------------+ 
| utf8_general_ci | 
+-------------------+ 
1 row in set (0.00 sec) 

Es muss einen Zwang gehen auf Grund der Satz meiner Sitzung ... so habe ich versucht, explizit passend:

UPDATE test SET text='abad' COLLATE utf8_unicode_ci WHERE id=1; 
UPDATE test SET text='abád' COLLATE utf8_unicode_ci WHERE id=1; 

Und immer noch habe ich die gleichen Ergebnisse (beide Male aktualisiert).

Vorerst bin ich mit meiner Annahme, dass InnoDB-Optimierung auf einer niedrigeren Ebene als SELECT nach Text-Kriterien getan wird.