Die Vergleiche und Sortierungen von Zeichenketten in MySQL sind abhängig von deren CHARACTER SET
sowie der gewählten COLLATION
. Neuere Versionen von MySQL, wie die hier verwendete Version 8.0.28, legen eine Datenbank, wenn nichts anderes verlangt wird mit der Codierung utf8mb4
und der COLLATION utf8mb4_0900_ai_ci
an.
Vergleiche und Sortierungen von Zeichenketten
Die Codierung utf8mb4
sagt aus, eine UTF-8-Zeichensatzcodierung mit bis zu vier Byte pro Zeichen zu nutzen. Der COLLATION
-Bezeichner kann analog einfach gelesen und interpretiert werden. Zuerst ist die Codierung angegeben. Mit 0900
wird auf den verwendeten Unicode-Sortieralgorithmus verwiesen. ai
steht für accent insensitive und ci
für case insensitive. Dementsprechend gibt es auch as
und cs
, was dann für die entsprechende Unterscheidung steht.
In einer Datenbank, die ohne entsprechende Optionen angelegt wird, gelten die obigen Einstellungen. Erstellen wir in einer solchen Datenbank eine Tabelle, die Zeichenketten enthält. Die Zeichenketten bestehen, um die Effekte offensichtlicher demonstrieren zu können, aus nur einem einzelnen Zeichen.
1 2 3 4 5 6 7 8 9 |
CREATE DATABASE mydb; USE mydb; DROP TABLE IF EXISTS strings; CREATE TABLE strings ( str VARCHAR(10) NOT NULL ); INSERT INTO strings (str) VALUES ('Ä'), ('ä'), ('A'), ('a'), ('À'), ('à'); |
Ohne weiter zu überlegen, würden wir ganz naiv sagen: „Ä ist von A verschieden“. Selbiges würden wir von den Zeichen à
und a
oder a
und A
behaupten. Das Ergebnis des folgenden SELECT
-Statement mag daher verwirren:
1 2 3 4 5 6 7 8 9 10 11 |
SELECT * FROM strings WHERE str = 'à'; | str --+----------------- 1 | Ä 2 | ä 3 | A 4 | a 5 | À 6 | à |
Augenscheinlich sieht MySQL diese Zeichenketten als gleich an. Folglich werden Sie auch bei einer Sortierung gleich behandelt.
1 2 3 4 5 6 7 8 9 10 11 |
SELECT * FROM strings ORDER BY str; | str --+----------------- 1 | Ä 2 | ä 3 | A 4 | a 5 | À 6 | à |
Oftmals ist das nicht das, was wir erwarten und/oder wünschen.
Explizite Festlegung der Vergleiche und Sortierungen von Zeichenketten
In MySQL können wir sehr flexibel bestimmen, wie Zeichenketten codiert und verglichen werden. Wir können dies bei der Definition der Datenbankobjekte (Datenbank, Tabelle, Tabellenfeld) oder auch bei der Abfrage festlegen.
Die von MySQL unterstützten Zeichensatzcodierungen und Vergleichs-/Sortieroptionen lassen sich mittels der folgenden Kommandos anzeigen.
1 2 |
SHOW CHARSET; # Zeichencodierungen SHOW COLLATION; # Vergleichs/-/Sortieroptionen |
Obgleich der Begriff COLLATION
auf die Sortierung abzielt, hat diese entscheidenden Einfluss auf Zeichenkettenvergleiche. Eine Sortierung basiert auf solchen Vergleichen, weshalb wir mit der Sortierung auch immer die Art und Weise der Vergleiche festlegen.
Datenbankobjekte
Eine Datenbank, die als Voreinstellung für alle Zeichenketten eine UTF-8-Codierung mit bis zu vier Byte pro Zeichen und die Regeln für eine deutsche Telefonbuchsortierung gemäß DIN 5700-2 nutzt, definieren wir beispielsweise so.
1 2 3 4 |
DROP DATABASE IF EXISTS mydb; CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_de_pb_0900_ai_ci; |
Die Optionen CHARACTER SET
und COLLATE
lassen viele Spielarten zu, können jedoch nicht beliebig kombiniert werden. So kann, was aber auch jedem offensichtlich sein sollte, ein Zeichensatz, der nur ein Byte verwendet nicht mit einer Vergleichs-/Sortieroption kombiniert werden, die auf einem Multi-Byte-Zeichensatz basiert.
Unabhängig davon, wie die Datenbank eingestellt ist, können einzelne Tabellen oder auch deren Spalten eine abweichende Zeichenbehandlung bekommen. Nehmen wir an, wir hätten unsere Datenbank so erstellt, wie soeben gezeigt. Darin wollen wir nun eine Tabelle anlegen, deren Zeichenketten jeweils mit nur einem Zeichen pro Byte gespeichert werden. Zeichenkettenvergleiche sollen westeuropäischen Konventionen unter Berücksichtigung von Groß-/Kleinschreibung erfolgen. Das schließt die Unterscheidung von Akzenten ein.
1 2 3 4 5 6 7 |
USE mydb; DROP TABLE IF EXISTS strings; CREATE TABLE strings ( str VARCHAR(10) NOT NULL ) CHARACTER SET latin1 COLLATE latin1_general_cs; INSERT INTO strings (str) VALUES ('Ä'), ('ä'), ('A'), ('a'), ('À'), ('à'); |
Die Standardsortierung für latin1
wäre latin1_swedish_ci, wie uns das Kommando SHOW CHARSET
verrät. Um unser Ziel zu erreichen, geben wir daher noch COLLATE latin1_general_cs
an. Nähere Erläuterungen zu Zeichensätzen und Vergleichs-/Sortieroptionen finden Sie in der MySQL-Dokumentation.
Versuchen Sie beispielsweise à
oder Ä
zu selektieren. Das wird Ihnen, wie erwartet, gelingen. Hätten Sie auf die Optionen bei der Tabellendefinition verzichtet, wäre dem nicht der Fall. Die gewählte Standardeinstellung der Datenbank mydb
, unterstützt dies in dieser Form nicht. Probieren Sie es aus! – Die Codierung utf8mb4
in Verbindung mit utf8mb4_de_pb_0900_ai_ci
behandelt a
, A
, à
und À
gleich. Das gilt vordergründig auch für die Zeichen ä
und Ä
. Diese werden jedoch, wie Sie gleich noch sehen werden, beim Sortieren anders behandelt.
Wir können Zeichensatz und Sortierung auf Tabellenebene aber auch auf Spaltenebene festlegen. Die vorstehenden Links verweisen auf die entsprechende MySQL-Dokumentationen.
Abfragen
Nun haben wir es nicht immer selbst in der Hand, bei der Anlage einer Datenbank oder Tabelle zu bestimmen, wie wir Zeichenketten behandelt wissen wollen. Nehmen wir an, wir hätten eine Standarddatenbank. In der sei wiederum die bereits bekannte Tabelle mit einigen Datensätzen.
1 2 3 4 5 6 7 8 9 |
DROP DATABASE IF EXISTS mydb; CREATE DATABASE mydb; USE mydb; DROP TABLE IF EXISTS strings; CREATE TABLE strings ( str VARCHAR(10) NOT NULL ); INSERT INTO strings (string) VALUES ('Ä'), ('ä'), ('A'), ('a'), ('À'), ('à'), ('b'), ('B'), ('ae'), ('Ae'), ('AE'), ('ab'); |
Diese wollen wir gemäß der DIN 5700-2, der deutschen Telefonbuchsortierung ausgeben. Eine Differenzierung nach Groß-Kleinschreibung wird hierbei nicht getroffen. Akzente werden ebenfalls ignoriert. Jedoch werden die deutschen Umlaute als zwei aufeinanderfolgende Vokale und ß
wie ss
behandelt. Wir erreichen das ganz einfach. Dazu geben wir der ORDER BY
-Klausel an, wie Sie bitte arbeiten soll.
1 2 |
SELECT * FROM strings ORDER BY str COLLATE utf8mb4_de_pb_0900_ai_ci; |
Wollen wir nur akzentuierte As (groß oder klein) selektieren, nutzt uns das noch nichts. Wir müssen hierzu den Zeichenkettenvergleich bei der Vergleichsoperation beeinflussen.
1 2 |
SELECT * FROM strings WHERE str = 'à'; |
Das Statement liefert offensichtlich alles, was auch nur näherungsweise dem Buchstaben A (groß oder klein) entspricht – auch Ä. Analog zur Sortierung können wir aber den Vergleich beeinflussen. Wir brauchen dazu eine Vergleichsoption, die die Akzente berücksichtigt, Groß- und Kleinschreibung jedoch ignoriert.
1 2 |
SELECT * FROM strings WHERE str = 'à' COLLATE utf8mb4_0900_as_ci; |
Fazit
Der Umgang mit Zeichensatzcodierungen sowie entsprechenden Vergleichs-/Sortieroptionen ist in MySQL denkbar einfach und äußerst flexible handhabbar.
Ein anderes Problem, welches in Zusammenhang mit Zeichenketten häufig auftritt, ist das sogenannte natürliche Sortieren. Lesen Sie hierzu den folgenden Beitrag.