Czym są systemy kodowania i porównań?
Jak wiadomo, podstawowy kod ASCII to tylko 128 znaków zapisywanych na siedmiu bitach. W tak małej liczbie nie mogą zmieścić się zatem litery narodowe, o alfabetach azjatyckich nie wspominając. Problem ominięto, tworząc strony kodowe, albo systemy kodowań. Jako że do dyspozycji został jeszcze jeden bit, dający dodatkowe 128 wolnych pozycji, mogą one je gospodarować według uznania. Obecnie standardem do zapisu liter na jednym bajcie jest ISO-8859 występujący w odmianach dla różnych regionów Europy/świata. Gdy jednak chcemy wymieszać ze sobą w jednym dokumencie teksty pochodzące z dwóch regionów, powstaje problem. Dwóch odmian ISO raczej nie da się pogodzić. Tu z pomocą wkracza Unicode. Do zapisu znaków wykorzystuje on więcej, niż jeden bajt, a to daje nam tysiące, a nawet miliony dozwolonych znaków do użycia w jednym dokumencie! Tu mamy z kolei dwie odmiany:
- UTF8 - podstawowe 128 znaków zapisywanych jest za pomocą jednego bajtu. Litery języków i alfabetów europejskich zapisywane są na dwóch bajtach, a znaki alfabetów azjatyckich - na trzech. Do powiadomienia, że zaczyna się kilkubajtowy znak, używa się wolnego ósmego bitu.
- UCS - wszystkie znaki zapisywane są na dwóch bajtach.
Kodowanie a MySQL
MySQL 4.0 i wcześniejsze nie obsługiwały systemów porównań i kodowania w ogóle. O ile do samego przechowywania tekstów nie robiło to wielkiej różnicy (jak nawala kodowanie, to wina jest nasza, a nie bazy), ale gdy przyszło cokolwiek posortować, zaczynały się schody. Do dziś wspominam rozmowy na forach dyskusyjnych w stylu "Jak zrobić sortowanie z uwzględnieniem polskich liter?" Rozwiązania były naprawdę przeróżne. Proponowano np. ich zamianę na znaki ASCII według reguły "ł -> lzz", "ą -> azz" itd. Oczywiście nie było to zbyt skuteczne. Obecnie moja rada na taki problem jest jedna: zainstaluj MySQL 4.1
Ustawienia od strony bazy
Przykładową konfigurację bazy opiszę na przykładzie UTF8. MySQL pozwala przydzielać kodowanie i system porównań na trzech poziomach: bazy, tabeli i pojedynczego pola. Niezwykle ważne jest, byśmy już na samym początku zdecydowali się na wybór jednego, GÓRA DWÓCH takowych, które użyjemy. Dlaczego? Otóż niektóre operacje nie pozwalają się wykonać, gdy wykonywane są na polach o różnych systemach porównań i należy z tym uważać. My wybierzemy "utf8_polish_ci" (można zamiast tego wziąć "utf8_unicode_ci", gdzie do sortowania użyty jest algorytm porównywania znaków Unicode), a dla pól z danymi binarnymi - "utf8_bin". Wybrane pozycje ustawiamy całej bazie, wszystkim tabelom oraz polom tekstowym (VARCHAR, CHAR, TEXT, BLOB).
Sprawa może skomplikować się, gdy przenosimy bazę z wcześniejszych wersji MySQL'a. Ten ustawia wtedy wszędzie domyślny system kodowania, niekoniecznie zgodny z naszymi zamiarami. Wtedy możemy zastosować taki wariant:
- Dzielimy listę zapytań na dwie części. Pierwsza niech tworzy strukturę, druga - zapisuje dane.
- Wgrywamy strukturę
- Odpalamy ręcznie napisany skrypt, który podmieni nam systemy kodowania. Przykładowy podaję pod tą instrukcją.
- Upewniamy się, czy phpMyAdmin pracuje na połączeniu z użyciem naszego systemu porównań (widoczne jest to na stronie głównej). Jeśli nie - zmieniamy.
- Wgrywamy dane
<?php // konfiguracja polaczenia $dbhost = 'localhost'; $dbuser = 'root'; $dbpass = 'root'; $dbname = 'test'; // na te kodowania bedziemy zamieniac $binary = 'utf8_bin'; $text = 'utf8_polish_ci'; $encoding = 'utf8'; // polecenia $n = mysqli_connect($dbhost, $dbuser, $dbpass); mysqli_select_db($n, $dbname); mysqli_query($n, 'ALTER DATABASE `'.$dbname.'` DEFAULT CHARACTER SET '.$encoding.' COLLATE '.$text); echo '<b>database `'.$dbname.'` converted to `'.$text.'`</b><br/>'; $r = mysqli_query($n, 'SHOW TABLE STATUS'); while($row = mysqli_fetch_assoc($r)) { if(strpos($row['Collation'], '_bin') !== FALSE) { mysqli_query($n, 'ALTER TABLE '.$dbname.'.'.$row['Name'].' DEFAULT CHARACTER SET '.$encoding.' COLLATE '.$binary); echo '`'.$row['Name'].'` converted from `'.$row['Collation'].'` to `'.$binary.'`<br/>'; } else { mysqli_query($n, 'ALTER TABLE '.$dbname.'.'.$row['Name'].' DEFAULT CHARACTER SET '.$encoding.' COLLATE '.$text); echo '`'.$row['Name'].'` converted from `'.$row['Collation'].'` to `'.$text.'`<br/>'; } $r2 = mysqli_query($n, 'SHOW FULL COLUMNS FROM '.$dbname.'.'.$row['Name']); while($row2 = mysqli_fetch_assoc($r2)) { if($row2['Collation'] != '') { if(strpos($row2['Collation'], '_bin') !== FALSE) { // pole binarne mysqli_query($n, 'ALTER TABLE `'.$row['Name'].'` CHANGE `'.$row2['Field'].'` `'.$row2['Field'].'` '.$row2['Type'].' CHARACTER SET '.$encoding.' COLLATE '.$binary.' '.($row2['Null'] != 'YES' ? 'NOT NULL' : 'NULL')); echo $row['Name'].'.'.$row2['Field'].' converted from `'.$row2['Collation'].'` to `'.$binary.'`<br/>'; } else { // pole tekstowe mysqli_query($n, 'ALTER TABLE `'.$row['Name'].'` CHANGE `'.$row2['Field'].'` `'.$row2['Field'].'` '.$row2['Type'].' CHARACTER SET '.$encoding.' COLLATE '.$text.' '.($row2['Null'] != 'YES' ? 'NOT NULL' : 'NULL')); echo $row['Name'].'.'.$row2['Field'].' converted from `'.$row2['Collation'].'` to `'.$text.'`<br/>'; } } } } mysqli_close($n); ?>
Ustawienia od strony skryptu
Kodowanie możemy przypisać także do połączenia z bazą danych. Ba, nie tylko możemy, a raczej musimy. Inaczej MySQL użyje domyślnego, który niekoniecznie będzie nam odpowiadać. Dlatego natychmiast po nawiązaniu połączenia należy wysłać takie zapytanie:
<?php mysql_query('SET NAMES \'utf8\''); ?>
header('Content-type: text/html;charset=utf-8');
Na tym kończę ten artykuł i życzę powodzenia w dalszych eksperymentach!














