dedlfix: utf-8 Zeichen in latin1_general_ci Datenbank speichern

Beitrag lesen

Tach!

  • Es gibt eine MySQL DB Tabelle deren Zeichensatz auf latin1_general_ci eingestellt ist.

Ausschlaggebend für die Daten ist letzlich die Kodierung der einzelnen (String-)Felder. Die Tabellenangabe ist nur ein Defaultwert für neu anzulegende Felder, wenn man dabei keine konkrete Angabe macht. (Das heißt auch, dass Felder derselben Tabelle auf unterschiedliche Kodierungen eingestellt sein können.)

Der nächste wichtige Punkt ist, dass die Kodierungsangabe der Daten nicht das alleinige zu beachtende Kriterium ist. Der MySQL-Server ist eine Blackbox. Die Feldkodierungsangabe gibt nur an, was für Daten drin abgelegt werden können. Der Weg von und zum Client steht auf einem anderen Blatt. Zwischen Client und Feld wird gegebenenfalls in beide Richtungen umkodiert. Was auf einer bestimmten Verbindung gesprochen wird, sollte nach (je)dem Aufbau ausgehandelt werden. Unterlässt man das, nimmt MySQL irgendeinen konfigurierten Defaultwert an. Spricht man dann was anderes, landen falsch kodierte Daten in den Feldern. Das muss nicht unbedingt auffallen, wenn man beim Rückweg denselben Fehler nur umgekehrt macht. Man sieht das nur daran, das Programme, die die Verbindungskodierung korrekt aushandeln (wie zum Beispiel phpMyAdmin), falsche Daten anzeigen.

Da die Verbindungskodierungsaushandlung beeinflusst, welche Kodierung man von und zum System verwenden kann/erhält, ist es ganz ungünstig, wenn man am Defaultwert Änderungen vornimmt und Clients im System hat, die sich bisher nicht darum geschert haben. Die bekommen dann natürlich Murks, und was sie senden, wird auch Murks. - Stellt man nur die Felder um, auf einen anderen Wert als die Default-Verbindungskodierung, dann klappt die Kommunikation mit diesen irgnoranten Clients weiterhin, bis auf Zeichen die nicht in dieser Kodierung dargestellt werden können. Beispielsweise sei die Default-Verbindungskodierung latin1 und die Felddaten utf8, dann können zwar Umlaute umkodiert werden, aber Nicht-latin1-Zeichen (wie kyrillische) gehen verloren. (In dem verlinkten Dokument stehen auch alle Konfigurationswerte aufgelistet, so dass du die Default-Werte in deinem System raussuchen kannst: beispielsweise im phpMyAdmin über "Variables".)

in meinem PHP Script so codieren, dass ich sie in der latin1_general_ci Tabelle speichern kann?

Die einzige Möglichkeit, 65536 Zeichen mit 256 möglichen Codes darzustellen, ist Ersatzdarstellungen zu verwenden. HTML macht das mit NCRs und Entitys, UTF-8 macht das mit seinen bekannten Bytesequenzen. (Ja, Unicode kennt mehr Zeichen, aber MySQLs utf8 kennt nur die BMP. Die anderen werden erst mit utf8mb4 ab MySQL 5.5 unterstützt - falls man das braucht.)

Wenn du in der Datenbank keinerlei Stringverarbeitung (zum Beispiel Sortieren und Vergleichen) betreibst, dann kannst du auch Ersatzdarstellungen mit mehreren Byte verwenden, wenn pro Zeichen nur eins vorgesehen ist. Beachte allerdings, dass die Feldlänge in dem Fall eine auf Byte begrenzende Wirkung kat (weil von ein Byte gleich ein Zeichen ausgegangen wird).

Eine mögliche Ersatzdarstellung wäre, wenn du UTF-8 zum MySQL sprichst, der aber von Latin1 ausgeht. Dann wird jedes Byte der UTF-8-Byte-Sequenzen als einzelnes Zeichen interpretiert. Beim Rückweg liest das UTF-8-System diese Einzel-Zeichen wieder als UTF-8-Bytesequenz, und alles scheint bestens - bis auf Stringverarbeitung im MySQL-System. Und es müsste dann generell so verfahren werden, also bereits enthaltene Latin1-Zeichen oberhalb von 0x7F müssten erstmal in UTF-8-Sequenzen umkodiert werden. Sämtliche Clients müssten ebenfalls so eingestellt sein, dass sie damit umgehen können. Insgesamt ist das aber auch nur eine Krücke, die man vermeiden möchte.

Maskieren. Auf dieselbe Art, wie man es anderswo tut. Bspw. prozent-codiert wie in URIs.
Vergiss das gleich wieder. Man möchte Daten nicht verfälschen. Stelle deine DB auf UTF-8 um.
Warum schlägst Du es dann erst vor?

Man kann auch mal aufzeigen, welche Wege zielführend erscheinen, es aber nicht wirklich sind.

Was sind die üblichen Verdächtigen bei solch einer Umstellung?

Die Umstellung kann misslingen, wenn Zeichenkodierungsangaben und Inhalt nicht übereinstimmen, also wenn bereits Murks abgelegt ist. (Wie erwähnt, phpMyAdmin verwenden. Zeigt der alles richtig an, ist in der Regel alles bestens.)

Wenn man die von latin1_general_ci auf utf-8 umgestellte DB wieder auf latin1_general_ci umstellt, erhält man dann wieder die ursprünglichen Daten?

Jein. Nur wenn das unmittelbar danach erfolgte. Sind bereits Zeichen jenseits von ISO-8859-1/Latin1 eingefügt worden, gehen diese verloren (werden zu Fragezeichen).

Um den Einwand vorweg zu nehmen: Ich mache natürlich eine Datensicherung der DB vor jeder "heikelen" Opertation.

Wenn du unter Datensicherung nicht das Kopieren der Tabellen verstehst, sondern einen Dump, dann wird dieser auch nur mit derselben Blackbox-Kommunikation wie bei einer normalen Client-Verbindung ausgeführt. Das heißt, dass dabei Umkodierungen stattfinden können, dabei denen Datenverlust auftreten kann.

Die Datensicherung durch Umkopieren (in eine zweite Tabelle oder Datenbank) kann auch durch passende SQL-Statements erfolgen (so wie der phpMyAdmin das macht). Das muss kein Umkopieren der Dateien sein, zu dem man das MySQL herunterfahren muss.

Generell ist noch zu sagen, dass du keine Freude haben wirst, die Datenbank auf UTF-8 umzustellen, wenn Clients im System sind, die mit UTF-8 nicht umgehen können. Die können zwar weiterhin Latin1/ISO-8859-1 mit dem Server sprechen, aber Zeichen außerhalb davon werden wegen der eingebauten Umkodierung als ? angezeigt, und wenn der Client solche Daten speichert, dann wird das ein wirklicher Verlust werden, weil dann nur das Fragezeichen im Feld landet.

dedlfix.