Powl: MySQL Charset

Hallo,

Es geht um ein Installationsskript in PHP, mit dem Tabellen in MySQL angelegt werden sollen.

Wenn ich in einer Datenbank, die als Standard in latin1 konfiguriert wurde, nach diesem Schema Tabellen erstellen lasse:

  
  $sql['tabelle'] = "CREATE TABLE tabelle (  
    ID       INT AUTO_INCREMENT PRIMARY KEY,  
    Tag      VARCHAR(10),  
    Zeit     VARCHAR(5) ,  
    .....  
  ) CHARACTER SET utf8 COLLATE utf8_general_ci;";  

reicht das dann, oder sollte besser die ganze Datenbank auf utf-8 umgestellt werden:

  
$sql['db_charset'] = "ALTER DATABASE myDatenbank DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci";  

?
PHP :

  
  foreach ($sql as $k => $w) {  
    if (!mysql_query($sql[$k], $db_link)) {  
      $ausgabe .= "<p>Fehler $k:<br />".mysql_error()."</p>";  
    } else {  
      $ausgabe .= "<p>Query $k erfolgreich.</p>";  
    }  
  }  

Eigentlich sollte es reichen, wenn die Tabellen entsprechend kodiert werden, oder?

netten Tag
^da Powl

--
===============================
powl.hat-gar-keine-homepage.de/
  1. echo $begrüßung;

    Wenn ich in einer Datenbank, die als Standard in latin1 konfiguriert wurde, nach diesem Schema Tabellen erstellen lasse:

    $sql['tabelle'] = "

    Was willst du denn hier mit PHP-Code bei einem reinen Datenbank-Problem?

    CREATE TABLE tabelle (
        .....
      ) CHARACTER SET utf8 COLLATE utf8_general_ci;";
    reicht das dann, oder sollte besser die ganze Datenbank auf utf-8 umgestellt werden:

    Wofür soll das reichen?

    Letzten Endes zählt die Kodierung des einzelnen Feldes. Und die dürfen durchaus innerhalb einer Tabelle verschieden kodiert sein.

    Die Angaben zur Datenbank und zur Tabelle sind nur Default-Angaben, die herangezogen werden, wenn für eine Tabelle oder ein Feld keine explizite Angabe zur Kodierung/Kollation gemacht wurde.

    Eigentlich sollte es reichen, wenn die Tabellen entsprechend kodiert werden, oder?

    Wie gesagt, die Feldkodierung ist ausschlaggebend. Und für den Transport der Daten von und zum Client ist die pro Verbindung auszuhandelnde Kodierung maßgebend. Das wird gern übersehen. Stichwörter: mysql_set_charset() oder auch SET NAMES.

    echo "$verabschiedung $name";

    1. Hallo,

      danke erstmal für die Antwort.

      Was willst du denn hier mit PHP-Code bei einem reinen Datenbank-Problem?

      Ich dachte besser zuviel Code als zuwenig ;)

      Wofür soll das reichen?

      Eigentlich möchte ich erreichen, dass auch für verschiedene Serverkonfigurationen eine Einstellung erreicht wird, die Sonderzeichen fehlerfrei darstellt.
      Z.B. soll bei der Installation eines Skriptes latin1 oder utf8 ausgeählt werden können. Gemäß dieser Auswahl soll eben der Charset für die Datenbanktabellen gesetzt werden.

      Letzten Endes zählt die Kodierung des einzelnen Feldes. Und die dürfen durchaus innerhalb einer Tabelle verschieden kodiert sein.

      Danke für den Hinweis. Aber in meinem Fall reicht eine einheitliche Lösung.

      Die Angaben zur Datenbank und zur Tabelle sind nur Default-Angaben, die herangezogen werden, wenn für eine Tabelle oder ein Feld keine explizite Angabe zur Kodierung/Kollation gemacht wurde.

      Somit kann ich mir die einzelnen Feldangaben ja sparen, wenn ich zuvor einmal für die ganze Tabelle den Standard (default) angegeben habe, richtig?

      Wie gesagt, die Feldkodierung ist ausschlaggebend. Und für den Transport der Daten von und zum Client ist die pro Verbindung auszuhandelnde Kodierung maßgebend. Das wird gern übersehen. Stichwörter: mysql_set_charset() oder auch SET NAMES.

      Danke für den Hinweis.
      Die Funktion mysql_set_charset() gibt es nicht in allen Versionen. Daher scheint der Weg über SET NAMES kompatibler, richtig?
      Ansonsten gibt es folgende Funktion (Quelle: http://de2.php.net/manual/de/function.mysql-set-charset.php):

        
      <?php  
       if (function_exists('mysql_set_charset') === false) {  
           /**  
            * Sets the client character set.  
            *  
            * Note: This function requires MySQL 5.0.7 or later.  
            *  
            * @see http://www.php.net/mysql-set-charset  
            * @param string $charset A valid character set name  
            * @param resource $link_identifier The MySQL connection  
            * @return TRUE on success or FALSE on failure  
            */  
           function mysql_set_charset($charset, $link_identifier = null)  
           {  
               if ($link_identifier == null) {  
                   return mysql_query('SET NAMES "'.$charset.'"');  
               } else {  
                   return mysql_query('SET NAMES "'.$charset.'"', $link_identifier);  
               }  
           }  
       }  
       ?>  
      
      

      ist das zu empfehlen? Oder sollte man in jedem Fall mit SET NAMES arbeiten? Kann es den Fall geben, dass in PHP

        
      function_exists('mysql_set_charset')  
      
      ~~~true zurückgibt, aber MySQL trotzdem einen Fehler produziert, weil die MySQL Version kleiner als 5.0.7 ist? Oder wird diese Funktion nur in PHP Verfügbar, wenn eine entsprechende MySQL Version installiert wurde?  
      Sind die MySQL Befehle in PHP also abhängig von der installierten PHP oder der installierten MySQL Version?  
        
      Ich fange,  
      wie man vielleicht merkt ;),  
      gerade erst an, mich mit MySQL zu beschäftigen. Das Quakenettutorial ist u.A. ganz gut und weitere Quellen auch. Diese Frage bezüglich der Charsets finde ich aber nirgendwo (für mich) verständlich und eindeutig erklärt. Vielleicht konstruiere ich auch ein Problem wo keines ist?  
        
      Und noch eine Frage, was passiert, wenn für die Verbindung Charset utf8 angegeben wurde, aber die Daten aus einem Formular stammen, das in einem HTML Dokument eingebettet ist, das als Charset latin1 angegeben hat?  
      Ich vermute, dass in diesem Fall mit Fehlern bei Umlauten und Sonderzeichen zu rechnen ist, richtig?  
      Ich vermute, der Charset muss dann bei allen involvierten Dokumenten einheitlich sein, richtig?  
        
      netten Tag  
      ^da Powl
      
      -- 
      ===============================  
      [powl.hat-gar-keine-homepage.de/](http://powl.hat-gar-keine-homepage.de/)
      
      1. echo $begrüßung;

        Eigentlich möchte ich erreichen, dass auch für verschiedene Serverkonfigurationen eine Einstellung erreicht wird, die Sonderzeichen fehlerfrei darstellt.
        Z.B. soll bei der Installation eines Skriptes latin1 oder utf8 ausgeählt werden können. Gemäß dieser Auswahl soll eben der Charset für die Datenbanktabellen gesetzt werden.

        Warum setzt du nicht einfach auf nur UTF-8? Solange mit den nicht-UTF-8-fähigen Systemen (PHP < 6 ist eins davon) keine Stringverarbeitung durchgeführt wird, bei der es auf die Zeichenkodierung ankommt (z.B. Anzahl der Zeichen ermitteln, Strings mitten im Wort trennen) fällt mir kein wirkliches Problem ein. 8-Bit-fähig sollten die Systeme jedoch mindestens sein.

        Die Angaben zur Datenbank und zur Tabelle sind nur Default-Angaben, die herangezogen werden, wenn für eine Tabelle oder ein Feld keine explizite Angabe zur Kodierung/Kollation gemacht wurde.
        Somit kann ich mir die einzelnen Feldangaben ja sparen, wenn ich zuvor einmal für die ganze Tabelle den Standard (default) angegeben habe, richtig?

        Dafür sind diese Default-Angaben vorgesehen. Es ist nur ein weit verbreiteter Irrtum, dass die Tabellen-Angabe für die Kodierung verantwortlich sei. Das dies nicht der Fall ist, sieht man deutlich, wenn man mal die Kodierung der Tabelle ändert. Die Felder ziehen dann nämlich nicht einfach nach.

        [...] Stichwörter: mysql_set_charset() oder auch SET NAMES.

        Hier meinte ich eigentlich die Funktion mysql_set_character_set() aus der MySQL-Client-API, nicht die PHP-Funktion mysql_set_charset(). Doch egal, der Unterschied ist nur eine Spitzfindigkeit.

        Die Funktion mysql_set_charset() gibt es nicht in allen Versionen.

        Das stimmt. Sie ist erst vor kurzer Zeit zur herkömmlichen mysql-Erweiterung PHPs hinzugefügt worden. Vorher gab es nur das mysqli-Pendant.

        Daher scheint der Weg über SET NAMES kompatibler, richtig?

        Jain (mehr ja als nein). Für LatinX und UTF-8 ist es kein Nachteil, SET NAMES statt mysql_set_character_set() zu verwenden. Es gibt aber mindestens eine asiatische Kodierung, die bei SET NAMES statt mysql_set_character_set() falsche Ergebnisse bei mysql_real_escape_string() produziert.

        * Note: This function requires MySQL 5.0.7 or later.

        Das Zeichensatz-/-kodierungskonzept wurde schon in Version 4.1 gründlich überarbeitet und im Wesentlichen auf den derzeitigen Stand gebracht. Frühere Versionen sind in der Lage UTF-8-kodierte Daten entgegenzunehmen und unverändert wieder herauszurücken, jedoch nicht sie zu interpretieren (z.B. für Stringverarbeitung). Die Funktion mysql_set_character_set() ist auch im 4.1-Zweig vorhanden, genauer: seit Version 4.1.13.

        ist das [eine Nachbildung von mysql_set_charset() für frühere PHP-Versionen] zu empfehlen? Oder sollte man in jedem Fall mit SET NAMES arbeiten?

        Sie arbeitet ja mit SET NAMES und SET NAMES gibt es seit MySQL-Version 4.1. Wenn du genau sein willst, kannst du nach dem Verbindungsaufbau zum MySQL-Server dessen Version abzufragen, und dann daraufhin mysql_set_charset() im Original oder im Nachbau anzuwenden. Solange man nicht unbedingt auf die UTF-8-Fähigkeit des Servers angewiesen ist, kann man im Prinzip den Funktionsnachbau ins Script aufnehmen, dann mysql_set_charset() aufrufen und das Ergebnis ignorieren. So sehr ich sonst das Ignorieren der Funktionsergebnisse anprangere, ist es in diesem Fall (nicht zwingend benötigte UTF-8-Fähigkeit) möglich. Die Applikation spricht UTF-8. Der Server interpretiert es oder auch nicht. Da die Daten unverändert wieder zurückkommen und von der Applikation als UTF-8 interpretiert werden, ist die Welt hier in Ordnung. Nur wenn andere Applikationen beteiligt sind, haben diese bei nicht vorhandener UTF-8-Fähigkeit des MySQL-Servers möglicherweise ein Problem.

        Die PHP-Funktionen der mysql- und mysqli-Erweiterung greifen auf eine Ausgabe der MySQL-Client-API zu. Es ist möglich, dass die Client-API aus einer anderen MySQL-Version stammt als der später anzusprechende Server. Man muss ja mit einer Client-Version verschiedene Server-Versionen ansprechen können. Der Versionsstand der Client-API kann also mit dem Server übereinstimmen, muss das aber nicht zwingend.

        Kann es den Fall geben, dass in PHP function_exists('mysql_set_charset') true zurückgibt, aber MySQL trotzdem einen Fehler produziert, weil die MySQL Version kleiner als 5.0.7 ist?

        (5.0.7 bzw. 4.1.13) Ja, denn PHP weiß ja vor einem Verbindungsaufbau zu einem bestimmten Server noch nicht, unter welche Version der läuft.

        Oder wird diese Funktion nur in PHP Verfügbar, wenn eine entsprechende MySQL Version installiert wurde?

        Der MySQL-Server spielt ja keine Rolle. Auch nicht beim Kompilieren PHPs. Nur die Client-API muss vorhanden sein. Um die Frage auf die Client-API umgemünzt zu beantworten, müsste ich in den Quellen PHPs recherchieren. Doch ich halte die Klärung dieser Frage nach meinen obigen Ausführungen für uninteressant.

        Sind die MySQL Befehle in PHP also abhängig von der installierten PHP oder der installierten MySQL Version?

        Sowohl als auch und weder noch. PHP kann ohne die MySQL-Extensions kompiliert werden. Und die MySQL-Extensions sind davon abhängig, welche MySQL-Client-API bei ihrer Erstellung mitgewirkt hat. Hinzu kommt ja dann noch die Verfügbarkeit der jeweiligen Funktionalität auf dem Server, den man anzusprechen gedenkt.

        Wenn alles auf der gleichen (Unix-)Maschine läuft, sind die Chancen, dass MySQL-Server- und -Client-API-Version übereinstimmen und zur PHP-Version passen sehr hoch.

        Ich fange, wie man vielleicht merkt ;), gerade erst an, mich mit MySQL zu beschäftigen. Das Quakenettutorial ist u.A. ganz gut und weitere Quellen auch. Diese Frage bezüglich der Charsets finde ich aber nirgendwo (für mich) verständlich und eindeutig erklärt.

        Dabei hab ich doch darüber schon oft genug das hiesige Archiv befüllt. :-)

        Und noch eine Frage, was passiert, wenn für die Verbindung Charset utf8 angegeben wurde, aber die Daten aus einem Formular stammen, das in einem HTML Dokument eingebettet ist, das als Charset latin1 angegeben hat?
        Ich vermute, dass in diesem Fall mit Fehlern bei Umlauten und Sonderzeichen zu rechnen ist, richtig?

        Ja, wer Kodierung A deklariert aber Kodierung B spricht, muss sich nicht über Verständigungsschwierigkeiten wundern. Das kann auch dann der Fall sein, wenn gar nichts deklariert wurde, und die Beteilgten von unterschiedlichen Kodierungen ausgehen.

        Ich vermute, der Charset muss dann bei allen involvierten Dokumenten einheitlich sein, richtig?

        Zeichensatz und Zeichenkodierung sind eigentlich zwei unterschiedlich Dinge, auch wenn sie oft als Synonym gebraucht werden und aus historischen Gründen charset=utf-8 notiert wird, obwohl es encoding heißen müsste. Unicode als Zeichensatz allein reicht nicht. Die Kodierung kann eine der UTF-Varianten sein. Darauf kommt es beim Kommunizieren an.

        An jeder Schnittstelle zwischen zwei Beteiligten einer mehrteiligen Verarbeitungskette muss über die zu verwendende Kodierung Klarheit herrschen. Ein Prozess, der zwischen zwei Schnittstellen mit unterschiedlicher Kodierung sitzt und arbeitet, muss gegebenenfalls Umkodierungen vornehmen. Da eine Umkodierung nicht in jedem Fall verlustfrei möglich ist, sollte man das zu vermeiden versuchen. Als einer der Beteiligten muss auch der Anwender betrachtet werden, der nicht wissen kann und muss, welche Zeichen unter welcher Kodierung nicht verarbeitet werden können, und sie daher eigentlich nicht zu verwenden hat.

        echo "$verabschiedung $name";

        1. Hallo,

          1000 Dank für Deine Mühe! Das ist nun deutlich klarer geworden.

          netten Tag
          ^da Powl

          --
          ===============================
          powl.hat-gar-keine-homepage.de/