Aus gegebenen Anlass erläuter ich heute das Encoding Handling von MySQL.
Der Experte unterscheidet zwischen:
1) Server Encoding
2) Client Encoding
3) Environment Encoding
Zum besseren Verständnis versuche ich das ganze bildlich an der Kommunikation mit Ausländern zu beschreiben. Stellt Euch einfach vor Ihr seid in Italien im Urlaub und möchtet von einem Italiener den Weg zu einer Sehenswürdigkeit wissen.
Environment Encoding
Das ist die Sprache, die Ihr standardmäßig sprecht. Als Deutscher also deutsch, als Franzose französisch, als Italiener italienisch, ...
Auch Computer haben eine Sprache, die sie standardmäßig sprechen. Für die Nutzung der MySQL CLI unter Windows ist es notwendig in der CMP das Kommando CHCP auszuführen. Das Ergebnis ist die Sprache, die hier für Windows plus MySQL CLI interessant ist. Ein deutsches Windows spricht hier Codepage 850 (CP850).
Unter Unix/Linux kommt es auf Euren Benutzer und dessen Einstellungen an. Auf modernen Linuxsystemen wird standardmäßig UTF8 verwendet, das muß aber nicht zwangsweise so sein.
Hier ist es außerdem noch wichtig, was Ihr genau für eine Umgebung verwendet. Als Beispiel: KDE-, Gnome-Terminal oder Konsole.
Bei einem Gnome Terminal gibt es oben unter Terminal den Punkt: Zeichenkodierung festlegen. Dort könnt Ihr sehen, welches Encoding das Terminal verwendet. Bei KDE gibt es oben in der Menüleiste irgendwo einen ähnlichen Punkt.
Solltet Ihr in Eurem Terminal nicht fündig werden, welches Encoding verwendet wird, so ist es zu 90% das Encoding was Euer Nutzer als locale verwendet und Ihr könnt es hier genauso rausfinden wie für die Konsolennutzung.
Auf der Konsole wird die Spracheinstellung von locale verwendet.
$ locale
hier sollte jetzt eine Ausgabe kommen, die Euch verrät, was verwendet wird.
Achtung unter Unix/Linux: Immer bedenken, wird hier nur C oder ASCII eingetragen, haben deutsche Umlaute 'äüöß' keine Chance. In diesem Fall solltet Ihr das Encoding erstmal ändern. Entweder auf ISO-8859-15 oder gleich auf UTF8.
Unter Windows habt Ihr keine Chance das Encoding zu ändern. Bei deutschen Windows wird CP850 verwendet und wenn Ihr z.B. Chinesische Zeichen speichern wollt, die von der Codepage nicht unterstützt werden, habt Ihr Pech. Abhilfe schafft hier entweder ein chinesisches Windows oder Ihr versucht Euer Glück mit Unix/Linux.
Wie sich Mac OS X hier verhält, weiss ich nicht.
Windows ist vergleichbar mit jemandem, der nur und ausschliesslich deutsch spricht. Also entweder spricht der Gegenüber in Italien deutsch oder die Auskunft nach dem Weg bleibt unverstanden.
Unix/Linux spricht deutsch als Muttersprache, kann aber zum Beispiel auch noch Englisch, Hebräisch oder Französisch. Wenn es hier zu Verständigungsschwierigkeiten kommt, kann auf eine andere Sprache gewechselt werden.
In der Praxis in der Computerwelt, wird dieser Sprachwechsel an dieser Stelle nur verzogen, wenn wirklich Zeichen gebraucht werden, die das Encoding nicht kennt oder wenn das Encoding ein nicht vom System (MySQL) unterstütztes Encoding ist.
Wenn der Italiener gegenüber deutsch versteht, versucht man ja auch erstmal deutsch mit ihm zu sprechen und wechselt nicht unbedingt sofort auf englisch. Spricht der Italiener nur italienisch und französisch und ich deutsch, englisch, französisch, dann sollte ich wohl oder übel auf französisch wechseln.
Die deutschen haben 'äöüß', die Schweden 'åø', die Tschechen noch ein paar andere Buchstaben und die Russen gleich ein komplett anderes Alphabet. Damit sich dieses unter einem Hut vereinen läßt, wurde UTF8 erfunden. UTF8 unterstützt den kompletten Zeichensatz von West- und Osteuropa plus noch Teile von Asien. Ein paar wenige Sprachen dieser Erde sind nicht mit UTF8 abgedeckt, aber dass ist in den meisten Fällen irrelevant.
Grob lässt sich sagen, UTF8 reicht für internationale Kommunikation völlig aus. Wenn Du UTF8 sprichst, kann Dich 98% der Erde verstehen. In der Computerwelt kann Dich Windows erstmal nicht verstehen. Aber das ist, wie nachfolgend klar wird, erstmal nicht schlimm.
Client Encoding
!!! ACHTUNG !!! Das nachfolgende gilt nur fürdie MySQL CLI und andere Clients die Daten binär weiterleiten. Connectors wie MyODBC oder JDBC oder auch Workbench oder die älteren MySQL GUI Tools regeln alles vollautomatisch und es sollte nichts manuell geändert werden. Bei PHP ist gründlich nachzudenken und nachzulesen. Siehe auch weiter unten.
Dein Hirn ist das Envrionment und Dein Mund ist der Client. Der Italiener ist der Server. Dein Hirn ist standardmäßig auf deutsch eingestellt, womit aus Deinem Munde deutsch herauskommt. Als erstes solltest Du herausfinden, ob der Italiener deutsch spricht:
Du: "Deutsch? English? Francais? Italiano?"
Er: "Deutsch, English, Italiano, Chinese, Hebrew"
In MySQL ist das ganze jetzt so:
Du weisst Dein Environment Encoding (sagen wir jetzt einfach es ist Codepage 850) und nun schaust Du ob dieses Encoding von MySQL unterstützt wird. Hierzu benutzt Du die CLI:
mysql> SHOW CHARSET;
Du findest heraus, dass es dort CP850 gibt. Das ist perfekt für Dich.
Benutzt Dein Environment UTF8 findest Du dort heraus, dass MySQL UTF8 unterstützt. Nutzt Dein System ISO-8859-15 dann stellst Du fest, das was am besten passt ist latin1.
Aber vorsicht hier unter Linux mit den Eurozeichen. Das ist genauso, als wenn der Italiener zwar perfekt Englisch aber nur gebrochen Deutsch spricht. Besser hier doch das Environment auf UTF8 stellen und in UTF8 kommunizieren. Bei dem Italiener würde man sich hier ja auch auf Englisch einigen.
Jetzt sagst Du dem Italiener, dass Du mit ihm in Deutsch kommunizieren möchtest:
Du: "Ok, dann sprechen wir in deutsch"
Wie ich oben sagte, der Italiener ist der Server und Dein Mund der Client.
Auch in MySQL solltest Du jetzt dem Server mitteilen, welche Sprache Du verwenden möchtest. In der CLI machst Du das mit dem Befehl:
mysql> SET NAMES <der_charset_der_zu_meinem_environment_encoding_passt>;
Unter deutschen Windows also:
mysql> SET NAMES CP850;
Für UTF8:
mysql> SET NAMES UTF8;
Nun weiß der Italiener, dass alles was von Dir gesagt wird, deutsch ist und er kann es völlig automatisch und transparent vestehen und/oder ggf. in Italienisch übersetzen.
Ein nettes Beispiel aus dem Leben, was passieren kann in Schriftform, wenn nicht vorher klar ist, welche Sprache verwendet wird:
Englisch oder Deutsch?
"die" => ist das jetzt das englische Wort für sterben oder der weibliche deutsche Artikel?
Das ganze wird erst klar, wenn man den gesamten Text liest oder wenn irgendwo vorher definiert wurde, dass der Text englisch/deutsch ist.
Einem Computer muß dieses stets vorher erläutert werden:
Du: "Achtung Computer jetzt kommt ein Text in englisch"
Computer: "ok"
Du: "Die"
Computer: "Nein, ich will noch nicht sterben."
Wenn Du weisst, dass viele Deiner Clients ein und dasselbe Environment Encoding nutzen, kannst Du das ganze auch in der my.cnf/my.ini machen.
Beispiel für UTF8:
[client]
default-character-set=UTF8
Client Encoding und PHP
Für PHP gilt erstmal dasselbe wie für die CLI. Doch bei PHP plus Webserver, Browser, Datenbank .... Da wird das Encoding des Browsers verwendet, was der Nutzer nutzt. Schwer herausfindbar. Die Empfehlung ist hier: PHP sollte daher dafür sorgen, dass der Browser stets UTF8 spricht. Der Webserver sollte UTF8 sprechen. Sämtliche Tabellen und Spalten in der Datenbank sollten UTF8 sein. Mit einem homogenem UTF8 System ist man stets auf der sicheren Seite.
Server Encoding
Wie bereits erwähnt, ist in meinem Beispiel der Italiener der Server.
Sein Hirn verarbeitet intern alles in Italienisch.
Er kann allerdings: "Deutsch, English, Italiano, Chinese, Hebrew"
Das heisst, Du gibst ihm zum Beispiel einen deutschen Text und er speichert ihn in seinem Hirn in Italienisch ab, oder er lässt ihn auf deutsch oder aber auch kann er Dir bei einer deutschen Frage auch eine englische Antwort geben. Oder wenn ihn der Chinese fragt, was Du gesagt hast, kann er es ihm in chinesisch geben.
Das ganze macht der Italiener völlig automatisch und transparent für Dich.
Wenn Du ihm sagst, speicher meinen deutschen Text doch bitte in englisch, so übersetzt er ihn und speichert ihn in englisch.
Genauso, wenn er die Wegbeschreibung für Dich in italienisch findet, überreicht er Dir die deutsche Übersetzung.
Gibst Du dem Italiener einen holländischen Text und hast ihm vorher gesagt, es sei ein deutscher Text, dann versucht er, alles in deutsch zu interpretieren und hat eine Reihe von Fragezeichen in seinen Augen. Wenn er dieses jetzt noch zu Vergleichszwecken ins Italienische übersetzen sollte, kommt noch mehr Müll dabei heraus.
Das Server Encoding ist das Encoding was der Server intern verwendet.
In der ganz untersten Ebene verwendet MySQL utf8. Das ist aber irrelevant. Bei einem Menschen ist mir für diese Ebene kein Name für die Sprache bekannt. In welcher Sprache denkst Du? Mein Hirn hat erst den Gedanken und versucht ihn dann in Worte zu fassen. In dem Moment, wo mein Hirn den ersten Ansatz des Gedanken hat, ist das ganze noch sprachlos. Irgendwelche Elektronen, die das Hirn erst in Worte fassen muss. Auf dieser Ebene spricht MySQL UTF8.
Die Ebenen darüber sind interessanter. Default verwendet MySQL hier LATIN1. Das ist historisch begründet. MySQL stammt einfach aus einer Zeit, bevor UTF8 als Default Sprache auf Unix/Linux Gang und Gebe war. Eine Änderung des Default Wertes in MySQL würde bedeuten, dass einfach zuviele alte Datenbanken/Anwendungen nicht mehr einwandfrei funktionieren würden.
Daher sollte man im Hinterkopf haben, diesen Default Wert einfach von Latin1 auf utf8 zu ändern.
Das geht in der my.cnf/my.ini mit:
[mysqld]
character-set-server=utf8
Das ganze lässt sich aber auch datenbankspezifisch festlegen, beim anlegen einer Datenbank:
CREATE DATABASE <eine_datenbank> CHARACTER SET UTF8;
Sofern jetzt nicht explizit etwas anderes gesagt wird, wird für alle Tabellen und Spallten dieser Datenbank zur Speicherung UTF8 verwendet.
Jedoch lässt sich bei MySQL das Encoding für die Speicherung bis zur Spalte herunter einzeln festlegen.
CREATE TABLE .... (....) CHARACTER SET UTF8;
CREATE TABLE ...(... <eine_spalte> VARCHAR(100) CHARACTER SET UTF8 ...) ...;
Sagen wir für unseren Italiener UTF8 sei englisch. Der Italiener bekommt einen deutschen Text, den übersetzt er nun völlig automatisch und transparent ins Englische und speichert ihn in englisch in seinem Hirn ab.
Egal was und wie der Italiener es hereinbekommt, sobald Übersetzungsregelungen vorliegen, übersetzt und speichert er es in dem gewünschten Format. Kennt er keine Überesetzungregel, speichert er ein Fragezeichen.
Das macht der Server auch. Wenn versucht wird ein Tschechisches Zeichen in einer LATIN1 Spalte zu speichern und LATIN1 kennt dieses Zeichen nicht, dann wird ein Fragezeichen gespeichert.
Wird versucht ein in CP850 eingebener Umlaut in UTF8 zu speichern, dann übersetzt der Server den Umlaut automatisch und transparent in UTF8. Möchte ich den Umlaut nun aus der Datenbank wieder bekommen und in CP850 angezeigt bekommen, so kümmert sich der Server hier völlig transparent darum, dass es nach meinen Wünschen geschieht.
Voraussetzung hierfür ist, dass das Client encoding auf den richtigen Wert gesetzt wurde.
Überprüfung
Welches Client Encoding ist eingestellt?
mysql> SHOW VARIABLES LIKE '%char%';
Die Variablen character_set_client, character_set_result, character_set_connection spiegeln das Client Encoding wieder. Die drei Variablen sollten niemals einzeln geändert werden. Nur durch SET NAMES oder default-character-set (in my.ini). Die drei Variablen sollten immer (ausser bei Connectors oder verwendeten MySQL Tools) alle drei auf denselben Wert gesetzt sein.
Wie finde ich heraus, ob mein Text richtig abgespeichert wurde?
Für deutsche ist das ein einfacher Test. Meistens geht es um LATIN1, UTF8 und ASCII. Ein Umlaut (egal ob ä, ö, ü oder ß) braucht in LATIN1 1 byte, in UTF8 2 byte und in ASCII ist es ein Fragezeichen.
Das Wort Bär hat in Latin1 die Länge 3 und in UTF8 die Länge 4.
mysql> SELECT LENGTH(<entsprechende_spalte>) FROM <entsprechende_tabelle>;
Wenn hier jetzt für das Wort 'Bär' 6 herauskommt, ist irgendwas falsch. Kommt hier 4 heraus und die Spalte ist Latin1, dann ist auch etwas falsch.
Falsch abgespeicherter Datensätze lassen sich nur sehr schwer bis gar nicht reparieren. Sobald MySQL angefangen hat, Fragezeichen statt Buchstaben abzuspeichern, ist alles vorbei. Keine Reparatur möglich.
Welchen Charset die Spalte zur Speicherung verwendet, lässt sich herausfinden mit:
mysql> SHOW CREATE TABLE <entsprechende_tabelle>;
In der Beschreibung steht es entweder direkt bei der Spalte, oder wenn dort nichts angegeben, am Ende der Tabelle. Ist nichts in der Spalte direkt angegeben, nimmt das System den Tabellencharset.
Falsch abgespeicherte Daten, bei denen keine Umwandlung in Fragezeichen passiert ist, lassen sich durch Spatenmodifizierung und den Umweg über eine binäre Zwischenspeicherung evtl. noch reparieren.
mysql> ALTER TABLE .... MODIFY .... VARBINARY(100) ....;
mysql> ALTER TABLE .... MODIFY ... VARCHAR(100) CHARSET ...;
Ratsam ist immer, erstmal eine Kopie vom Original zu machen und mit der Kopie die Schritte auszuprobieren.
Außer LENGTH() kann auch noch die Funktion HEX() zum Troubleshooting herangezogen werden.
LENGTH() gibt stets in MySQL die Länge in Bytes zurück.
Zusammenfassung:
Auf Windows stets SET NAMES CP850 verwenden, bevor irgendetwas ausgeführt wird.
Auf anderen Systemen erst das Environment Encoding herausfinden und dann SET NAMES <mein_encoding> verwenden.
Erst nachdenken, bevor Daten falsch abgespeichert werden.
Weitere Links:
http://forge.mysql.com/w/images/b/b6/How_to_Use_Charsets_and_Collations_Properly.pdf
