Layer 8Ehrlicher Tech-Support

Namen mit Umlauten sortieren nicht dort, wo du suchst

Die Liste ist alphabetisch, aber Ärzte steht hinter Zylinder. Alphabetische Reihenfolge ist keine Eigenschaft der Zeichen, sondern der Sprache.

Aufwand:
ca. 25 Min.
Voraussetzung:
Eine Kommandozeile mit sort, Zugriff auf die Locale-Einstellung des Servers, Die Kollationseinstellung der Datenbank
Gilt für:
Linux mit glibc-Locales, PostgreSQL 18, SQLite, Jede Sortierung über Zeichenketten mit Umlauten oder Akzenten

Das Symptom

Die Mitgliederliste ist alphabetisch sortiert, aber Frau Öztürk steht hinter Herrn Zylinder, ganz unten, hinter allem. Oder die Liste zerfällt in zwei Hälften: erst alle Namen mit großem Anfangsbuchstaben, dann alle mit kleinem. Oder – der teure Fall – jemand sucht einen Kunden, findet ihn nicht, legt ihn neu an, und ab diesem Tag existiert derselbe Mensch zweimal im System.

Besonders verwirrend ist die Version, in der beide Beteiligten recht haben: Der Bericht vom Server sortiert anders als derselbe Bericht vom Arbeitsplatzrechner, mit denselben Daten und derselben Software.

Die „Layer 8“-Wahrheit

„Alphabetisch“ ist keine Eigenschaft der Buchstaben, sondern eine Regel, die jemand aufgestellt hat – und es gibt mehrere. Der Unicode-Standard für Kollation sagt es im ersten Absatz: Deutsche, Franzosen und Schweden sortieren dieselben Zeichen unterschiedlich. Ein ä steht im Schwedischen hinter dem z und im Deutschen neben dem a. Sogar innerhalb einer Sprache gibt es mehr als eine richtige Antwort: Dasselbe Dokument führt deutsche Wörterbuch- und deutsche Telefonbuchordnung als zwei verschiedene Reihenfolgen auf.

Wenn die Umlaute ganz am Ende landen, ist gar keine Sprache im Spiel: Dann wird nach Zahlenwerten sortiert, und in UTF-8 liegen alle Nicht-ASCII-Zeichen hinter allen ASCII-Zeichen. Die Handbuchseite von sort sagt genau das, mit drei Sternchen davor. Diese Reihenfolge ist schnell, stabil und für Maschinen ideal – und für jede Liste, die ein Mensch liest, unbrauchbar.

Der Layer-8-Anteil ist der Reflex, das für einen Schönheitsfehler zu halten. Ist es nicht. Der Unicode-Standard beschreibt selbst den Fall, auf den es hinausläuft: Ein Nutzer sucht einen Namen dort, wo er ihn erwartet, findet ihn nicht und hält ihn für nicht vorhanden – bei einer Datenbankabfrage merkt er nicht einmal, welche Datensätze fehlen. Aus einer Sortierfrage wird so eine Doppelanlage, eine verschickte Mahnung an den falschen Kunden oder ein Bericht mit Lücke. Deshalb gehört die Sortierregel dorthin, wo sortiert wird, ausdrücklich hingeschrieben, statt sie sich von der Umgebung des jeweiligen Rechners geben zu lassen.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Zuerst feststellen, nach welcher Regel gerade sortiert wird

    Die Reihenfolge kommt nicht aus dem Programm, sondern aus der Umgebung. Ein Blick auf die Locale-Variablen sagt, welche Sprache gerade gilt – und erklärt in den meisten Fällen sofort, warum Server und Arbeitsplatz zwei verschiedene Listen erzeugen.

    locale | grep -E 'LC_ALL|LC_COLLATE|LANG='
    # LANG=de_DE.UTF-8
    # LC_COLLATE="de_DE.UTF-8"
    # LC_ALL=
    
  2. 2.

    Den Unterschied an derselben Datei sichtbar machen

    Drei Aufrufe, eine Datei, drei Reihenfolgen. Das ist der Beweis, den man in jeder Diskussion braucht: Nicht die Daten sind unterschiedlich, sondern die Regel. Unter C landen die Umlaute am Ende, weil dort nach Zahlenwerten sortiert wird; unter deutscher Locale stehen sie beim Grundbuchstaben; unter schwedischer hinter dem Z, wie es UTS #10 beschreibt.

    printf 'Zylinder\nAerzte\nÄrzte\nOrder\nÖrtlich\nZuse\n' > /tmp/n.txt
    
    LC_ALL=C sort /tmp/n.txt
    # Aerzte / Order / Zuse / Zylinder / Ärzte / Örtlich
    
    LC_ALL=de_DE.UTF-8 sort /tmp/n.txt
    # Aerzte / Ärzte / Order / Örtlich / Zuse / Zylinder
    
    LC_ALL=sv_SE.UTF-8 sort /tmp/n.txt
    # Aerzte / Order / Zuse / Zylinder / Ärzte / Örtlich
    
  3. 3.

    Die Groß- und Kleinschreibung als zweiten Effekt prüfen

    Dieselbe Umschaltung verändert auch, wie Groß- und Kleinschreibung behandelt wird. Nach Zahlenwerten kommen erst alle großen, dann alle kleinen Buchstaben – eine Namensliste zerfällt damit in zwei Blöcke. Unter einer Sprach-Locale stehen die Wörter dort, wo ein Mensch sie sucht.

    printf 'apfel\nApfel\nBirne\nbanane\n' > /tmp/gk.txt
    
    LC_ALL=C sort /tmp/gk.txt
    # Apfel / Birne / apfel / banane
    
    LC_ALL=de_DE.UTF-8 sort /tmp/gk.txt
    # apfel / Apfel / banane / Birne
    
  4. 4.

    Prüfen, ob die Programmiersprache überhaupt sprachbewusst sortiert

    Viele eingebaute Sortierfunktionen sortieren nach Codepunkten, nicht nach Sprache – und liefern damit dasselbe Ergebnis wie die Byte-Sortierung. Das ist kein Fehler der Sprache, sondern eine Voreinstellung, die man kennen muss. Für Listen, die Menschen lesen, braucht man ausdrücklich eine sprachbewusste Sortierung.

    python3 -c "print(sorted(['Zylinder','Ärzte','Aerzte','Örtlich','Order']))"
    # ['Aerzte', 'Order', 'Zylinder', 'Ärzte', 'Örtlich']
    
  5. 5.

    Die Kollation dort festlegen, wo sortiert wird

    Verlass dich nie darauf, dass der Server dieselbe Locale hat wie dein Rechner. In PostgreSQL wird die Kollation an der Spalte oder direkt an der Abfrage gesetzt, damit die Reihenfolge nicht mit der Umgebung wandert. Wichtig ist die Absicht: Eine Liste für Menschen bekommt eine Sprachkollation, ein technischer Schlüssel bekommt die stabile Byte-Kollation.

    SELECT name FROM kunden ORDER BY name COLLATE "de-DE-x-icu";
    SELECT collname FROM pg_collation WHERE collname LIKE 'de%';
    
  6. 6.

    Sortierung und Suche nicht durcheinanderbringen

    Zwei getrennte Fragen: Wo steht der Eintrag in der Liste, und findet ihn die Suche? Die zweite ist die wichtigere, weil ein nicht gefundener Datensatz aussieht wie ein nicht vorhandener. Für die Suche gilt deshalb: Bei Umlauten und Akzenten nicht auf eine ASCII-Faltung wie SQLites NOCASE verlassen, sondern entweder eine passende Kollation oder eine ausdrücklich normalisierte Suchspalte verwenden.

  7. 7.

    Beim Serverumzug die Locale mitnehmen und die Indizes neu bauen

    Eine Datenbank, die auf ein System mit anderer Locale wandert, sortiert danach anders – und Indizes, die nach der alten Regel gebaut wurden, passen dann nicht mehr zu den Vergleichen, die nach der neuen Regel gestellt werden. Deshalb gehört die Locale in die Umzugscheckliste, und nach dem Umzug werden die Indizes über Textspalten neu aufgebaut. Das ist billiger als die Suche nach einem Datensatz, den es angeblich nicht gibt.

  8. 8.

    Mit Namen testen, die nicht aus dem Duden stammen

    In den Testbestand gehören Umlaute, ein scharfes S, ein französischer Akzent, ein skandinavisches ø und ein slowakisches ch. Die Handbuchseite zu den Locales nennt gleich ein Beispiel, das man sonst übersieht: Unter LC_COLLATE werde das deutsche scharfe S wie ss sortiert. Wer nur Meier und Schmidt testet, sieht von alldem nichts – und liefert eine Liste aus, die für jeden zweiten Nachnamen falsch ist.

Häufige Fragen (FAQ)

?Es ist doch das Alphabet. Wie kann eine Sortierung falsch sein?

Weil es nicht ein Alphabet gibt, sondern eines pro Sprache. Der Unicode-Standard zur Kollation, UTS #10, sagt das im ersten Abschnitt: „Collation varies according to language and culture: Germans, French and Swedes sort the same characters differently.“ Dasselbe Dokument nennt das konkrete Beispiel: Schwedisch behandelt ä als eigenen Buchstaben und sortiert ihn hinter z, Deutsch sortiert ihn entweder wie ae oder wie andere Abwandlungen des a und damit direkt hinter a. Selbst nachgemessen mit `sort`: Bei schwedischer Locale steht Ärzte hinter Zylinder, bei deutscher zwischen Aerzte und Order. Beide Listen sind korrekt sortiert – nach verschiedenen Regeln.

?Woher kommt die Reihenfolge, in der alle Großbuchstaben vorn stehen?

Das ist keine Sprache, sondern gar keine: Es ist die Sortierung nach Zahlenwerten. Die Handbuchseite von `sort` warnt genau davor: „*** WARNING *** The locale specified by the environment affects sort order. Set LC_ALL=C to get the traditional sort order that uses native byte values.“ Bei dieser Byte-Sortierung stehen alle Großbuchstaben vor allen Kleinbuchstaben, und alles außerhalb von ASCII landet am Ende. PostgreSQL beschreibt seine C-Kollation genauso: Sie sortiere „by byte values rather than natural language order“, und nur die ASCII-Buchstaben A bis Z würden überhaupt als Buchstaben behandelt. Wer eine Liste für Menschen erzeugt, will das nie.

?Warum findet die Suche einen Namen nicht, der offensichtlich in der Liste steht?

Weil er an einer anderen Stelle einsortiert wurde, als der Suchende annimmt – und UTS #10 beschreibt genau diesen Ablauf als Schadensfall. In den meisten lateinschriftlichen Sprachen sortiere ø als Abwandlung von o, in Norwegisch und Dänisch dagegen als eigener Buchstabe hinter z. Der Standard schildert die Folge: „A user will look for ‚Søren‘ between ‚Sorem‘ and ‚Soret‘, not see it in the selection, and assume the string is missing“. Und weiter, für den Fall der Datenbankabfrage: „in a database selection the user may not realize what records are missing“. Nicht gefunden heißt hier eben nicht nicht vorhanden – der teuerste Irrtum in dieser ganzen Geschichte.

?Ich vergleiche doch ohne Rücksicht auf Groß- und Kleinschreibung. Reicht das nicht?

Nur für ASCII, und das ist der Haken. SQLite dokumentiert seine Kollation NOCASE unmissverständlich: Sie falte „the 26 upper case characters of ASCII“ auf ihre Kleinbuchstaben, und ausdrücklich: „Note that only ASCII characters are case folded. SQLite does not attempt to do full UTF case folding due to the size of the tables required.“ Eine Suche nach müller findet damit MÜLLER nicht, weil Ü und ü für diese Regel zwei verschiedene Zeichen sind. Der Fehler ist besonders unangenehm, weil er in jedem Test mit englischen Namen besteht.

?Kann ich das nicht einfach in der Datenbank einstellen und gut ist?

Einstellen ja, aber nicht nachträglich und nicht folgenlos. Die PostgreSQL-Dokumentation nennt den Grund für das Kollations-Merkmal an Spalten und Ausdrücken: Es hebe „the restriction that the LC_COLLATE and LC_CTYPE settings of a database cannot be changed after its creation“ auf. Anders gesagt: Die Grundeinstellung fällt beim Anlegen der Datenbank und bleibt. Wer sie später anders braucht, setzt sie je Spalte oder je Abfrage – und muss wissen, dass Indizes an der Kollation hängen, mit der sie gebaut wurden. Ein Serverumzug auf ein System mit anderer Locale ist deshalb kein reines Kopieren, sondern ein Eingriff in jede sortierte Abfrage.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler