ugrás a tartalomhoz

MySQL 5.0: karakterkódolások

Bártházi András · 2005. Nov. 20. (V), 23.00
MySQL 5.0: karakterkódolások
A MySQL 4.1-es verziójának egyik újdonsága a fejlett karakterkódolás támogatás volt. Most, az 5.0-s verzió megjelenésével sokan kerülnek szembe a kérdéssel: hogyan, mire lehet ezt a lehetőséget használni, s milyen beállítások szükségesek ehhez, egyáltalán: mire kell odafigyelni ezzel kapcsolatosan. A cikkben ezt a kérdéskört próbálom meg körbejárni, példákkal illusztrálva a lehetőségeket.

A MySQL 5.0 dokumentációja nem túl bőbeszédű azt illetően, hogy pontosan hogyan is működnek benne a karakterkészletek, itt, a Weblaboron is már több kérdés foglalkozott a fórumunkban azzal a kérdéssel, hogy pontosan mi is történik, és miért nem működnek jól az ékezetek. A következőkben leírtakat részben a hivatalos dokumentáció, részben pedig egy Debian Linuxon futó MySQL 5.0.15-ös adatbázison végzett próbálkozásaim alapján állapítottam meg, s bár próbáltam alaposan tesztelni minden megközelítést, de előfordulhat, hogy néhány megállapítás nem lesz helyes. Bár részletesebben nem ástam magam bele a dologba, de a Windows-os MySQL 5.0.15-tel tapasztaltam furcsaságokat. Szívesen veszek minden hozzászólást, tapasztalatot.

Mielőtt áttekintenénk a MySQL lehetőségeit, nem árt tisztázni, hogy mi is az a karakter kódolás, mi és hogyan zajlik a háttérben, s milyen alapismeretek szükségesek ebben a témakörben. Ez a rövid (és szükségszerűen számos kérdésben egyszerűsített leírást adó) bevezető talán nem csak a MySQL-lel ismerkedőknek lesz hasznos. Ezt a részt át lehet ugrani, ha valaki úgy érzi, hogy mindent tud a karakterkészletekről, de azt kell mondjam, hogy valószínűleg még annak is fog újdonságot mondani, aki ezt hiszi magáról (én így jártam az adatgyűjtés során).

Karakterkészletek, kódolások

Egy karakter alatt bármilyen írásjelet, betűt, számot, vagyis szimbólumot érthetünk. Ha papírra vetünk egy szimbólumot, akkor elsősorban a rajzolt alak fogja meghatározni, hogy pontosan mit is írtunk le. Az író fejében létezik egy szimbólum, s ahhoz egy közös megállapodás szerint egy rajzolt képet rendel az írás során, az olvasó pedig ezt olvassa el, s értelmezi ezt a szimbólumot. A számítógépek esetén sincsen másként ez a dolog, azonban a technikai korlátoknak köszönhetően többfajta megoldás áll rendelkezésre a karakterek tárolására - hasonlóan ahhoz, hogy ha leírunk egy H betűt, akkor az olvasó nem tudhatja, hogy egy H betűt, vagy egy cirill "N" betűt írtunk le.

A számítógépek esetében egyesek és nullák sorozatával, azaz bitek, bájtok segítségével tudunk letárolni egy adott szimbólumot. Adott szimbólumok összességét, illetve azt, hogy hogyan lehet letárolni ezeket számítógéppel, karakterkészletnek, a letárolás folyamatát pedig kódolásnak hívjuk. Az egyik legismertebb karakterkészlet az ASCII, mely 128 különböző szimbólumot tartalmaz, s 7 biten tárolja le ezeket, például az "A" betűhöz a 65-ös kódot rendeli. A magyarok számára fontos készlet ennek bővítése, a szabványban is rögzített iso-8859-2, rövidebben és kimondhatóbban a Latin2 "Central-European" (közép-európai) karakterkészlet. Ez már tartalmazza az ékezetes karaktereket is, s 8 biten, azaz egy byte-on tárol le egy karaktert.

A nemzetköziség kapcsán egyre inkább teret nyer azonban a Unicode karakterkészlet, melynek az egyik legismertebb kódolási módja az UTF-8. A Unicode, ahogy minden más karakterkészlet is, minden egyes karakterhez egy számot (úgynevezett kódpont) rendel hozzá, azonban az, hogy ez milyen formában kerül tárolásra, már az alkalmazott kódolási formától függ (más karakterkészleteknél átalában egyfajta ábrázolásról beszélhetünk, itt több is van). A Unicode alap kódkészlete 32 bites, azaz 4294967296 különböző szimbólum tárolását tenné lehetővé, azonban a tervek szerint ebből csak 21 bitnyit fognak kihasználni, így valamivel több mint egymillió különböző szimbólumról rendelkezhet majd. Az egyes szimbólumok hozzárendelése az újabb és újabb Unicode változatok megjelentetésével történik. A Unicode karakterkészletet úgynevezett plane-ekre (fordítható így: metszetekre) osztották az egyes kódpontok utolsó 16 bitje mentén. A nullás metszetbe, az úgynevezett Base Multilingual Plane-be (kb.: alapvető többnyelvű metszet) a lényegesebb szimbólumok kerültek, gyakorlatilag minden, aminek a használata felmerülhet az esetek többségében. A többi metszeten történelmi írásmódok, tudományos jelölések kaptak helyet.

Az UTF-8 kódolás ezeket a számokat (melyek egy-egy szimbólumot takarnak) változó hosszúságon kódolja. A legrövidebb UTF-8-as karakter egy bájtos, a leghosszabb négy bájtos. Az ASCII karakterek megegyeznek az egy bájtos UTF-8 karakterekkel, ez az UTF-8 egyik legnagyobb előnye (az angol karakterkészletet tömören és ASCII kompatibilisen teszi tárolhatóvá).
A Unicode-ról és az UTF-8 kódolásról sokminden érdekességet, további információt le lehetne még írni, illetve a most leírtak csak egy nagyon durva áttekintést nyújtottak. Linkként magát a Unicode szabvány oldalát tudom ajánlani: http://www.unicode.org, illetve egy UTF-8/Unicode FAQ-ot, ahonnan én is sokat okultam: http://www.cl.cam.ac.uk/~mgk25/unicode.html.

A karakterkészletek száma elég nagy, nem is vállalkozom arra, hogy megpróbáljam felsorolni az érdekesebbeket, szóba jöhetőket. A MySQL valószínűleg ismerni fogja azt a kódolást, melyet választani fogunk, ha nem, akkor magunk is készíthetünk hozzá leírást a kódolásról. Amit az előzőkben bemutatottakból leszűrhetünk: az ASCII karakterkészletben szereplő karaktereket a legtöbb általánosan használt kódolás (ASCII, ISO-8859-x, UTF-8) ugyanúgy tárolja, ha csak és kizárólag ékezet nélküli angol karaktereket, számokat tartalmaz egy fájl valamely kódolással ezek közül, akkor nem lehet megállapítani, hogy melyik kódolást használtuk. A különbség, és így a problémák is az ékezetes betűkkel jönnek elő.

A következő táblázat pár szimbólum tárolt formáját mutatja be:

*ASCIILatin1Latin2UTF-8
a61616161
á-A0A0C3A1
õ-F5-C3B5
ő--F5C591


Mint látható, az "a" karakter mind a négy bemutatott karakterkészletben (ASCII, Latin1, Latin2 és UTF-8) szerepel, sőt, ugyanaz a tárolási forma is. Ebből levonhatjuk a következtetést amit az előbb is említettem: ha csak angol betűket szeretnénk tárolni (és számokat és az alapvető írásjeleket), akkor teljesen mindegy, hogy milyen kódolást használunk (többnyire).

A "hullámos õ" karakter a Latin1 (azaz Western European) kódolásban szerepel, illetve az UTF-8 kódolásban található még meg. Az ASCII karakterkészletbe "nem fér bele", a Latin2 karakterkészlettel összehasonlítva pedig látható, hogy a Latin1 segítségével lekódolt "hullámos ô" ott mást, pontosabban egy "ő" karaktert jelöl (vagyis ugyanazt a byte-ot más szimbólum jelölésére használjuk). És máris áttértünk az "ő" karakterre, mely Latin2-es és UTF-8 kódolással írható le.

A fentiekből kiderül, hogy a lehető legtöbb karaktert akkor tudjuk letárolni, ha a UTF-8 karakterkódolást választjuk. Ha csak egy nyelvet szeretnénk letárolni, s azon belül sem szeretnénk a speciális adott nyelvre jellemző idézőjeleket (például magyarban: „»«”), akkor választhatjuk valamely Latin kódolást, de ellenkező esetben a legkényelmesebb az lehet, ha az UTF-8 mellett döntünk.

Az UTF-8 karaktereket az elterjedt operációs rendszerek és böngészők kiválóan támogatják, például a Weblabor is UTF-8 kódolást használ oldalain. A problémánk esetleg a szerver oldali programozási nyelvvekkel lehet, melyek lehet, hogy nem támogatják, részben támogatják, vagy kiválóan támogatják ezt a kódolást. A PHP-ról elmondható, hogy részben támogatja, mert tárolni és megjeleníteni minden további nélkül tudja (nem veszi figyelembe a kódolást), sztring műveleteket pedig az mbstring kiterjesztéssel tud végrehajtani. A Perl legújabb verziója már jól támogatja a kódolásokat (belsőleg tárolni képes, hogy milyen kódolású egy sztring, s a műveletek ennek függvényében hajtódnak végre), a Java pedig talán az indulása óta alapvetően Unicode alapokon működik.

Ennyi bevezető után, mely remélem sokak számára hasznos volt, rátérhetünk arra, hogy az egészhez hogyan is kapcsolódik a MySQL, s milyen lehetőségeink vannak...

A MySQL karakterkódolásai

A MySQL szöveges mezőiben különböző kódolású szövegek tárolására, a különböző kódolások közötti konvertálásra, illetve ezeknek a szövegeknek a rendezésére (ABC szerinti összehasonlítására) képes. Ha egy MySQL konzolra belépünk, akkor az adott MySQL példány által ismert karakterkészleteket a következőképpen tudjuk lekérdezni:
mysql> show character set;
+----------+-----------------------------+---------------------+--------+
| Charset  | Description                 | Default collation   | Maxlen |
+----------+-----------------------------+---------------------+--------+
| big5     | Big5 Traditional Chinese    | big5_chinese_ci     |      2 |
| dec8     | DEC West European           | dec8_swedish_ci     |      1 |
| cp850    | DOS West European           | cp850_general_ci    |      1 |
| hp8      | HP West European            | hp8_english_ci      |      1 |
| koi8r    | KOI8-R Relcom Russian       | koi8r_general_ci    |      1 |
| latin1   | cp1252 West European        | latin1_swedish_ci   |      1 |
| latin2   | ISO 8859-2 Central European | latin2_general_ci   |      1 |
| swe7     | 7bit Swedish                | swe7_swedish_ci     |      1 |
| ascii    | US ASCII                    | ascii_general_ci    |      1 |
| ujis     | EUC-JP Japanese             | ujis_japanese_ci    |      3 |
| sjis     | Shift-JIS Japanese          | sjis_japanese_ci    |      2 |
| hebrew   | ISO 8859-8 Hebrew           | hebrew_general_ci   |      1 |
| tis620   | TIS620 Thai                 | tis620_thai_ci      |      1 |
| euckr    | EUC-KR Korean               | euckr_korean_ci     |      2 |
| koi8u    | KOI8-U Ukrainian            | koi8u_general_ci    |      1 |
| gb2312   | GB2312 Simplified Chinese   | gb2312_chinese_ci   |      2 |
| greek    | ISO 8859-7 Greek            | greek_general_ci    |      1 |
| cp1250   | Windows Central European    | cp1250_general_ci   |      1 |
| gbk      | GBK Simplified Chinese      | gbk_chinese_ci      |      2 |
| latin5   | ISO 8859-9 Turkish          | latin5_turkish_ci   |      1 |
| armscii8 | ARMSCII-8 Armenian          | armscii8_general_ci |      1 |
| utf8     | UTF-8 Unicode               | utf8_general_ci     |      3 |
| ucs2     | UCS-2 Unicode               | ucs2_general_ci     |      2 |
| cp866    | DOS Russian                 | cp866_general_ci    |      1 |
| keybcs2  | DOS Kamenicky Czech-Slovak  | keybcs2_general_ci  |      1 |
| macce    | Mac Central European        | macce_general_ci    |      1 |
| macroman | Mac West European           | macroman_general_ci |      1 |
| cp852    | DOS Central European        | cp852_general_ci    |      1 |
| latin7   | ISO 8859-13 Baltic          | latin7_general_ci   |      1 |
| cp1251   | Windows Cyrillic            | cp1251_general_ci   |      1 |
| cp1256   | Windows Arabic              | cp1256_general_ci   |      1 |
| cp1257   | Windows Baltic              | cp1257_general_ci   |      1 |
| binary   | Binary pseudo charset       | binary              |      1 |
| geostd8  | GEOSTD8 Georgian            | geostd8_general_ci  |      1 |
| cp932    | SJIS for Windows Japanese   | cp932_japanese_ci   |      2 |
| eucjpms  | UJIS for Windows Japanese   | eucjpms_japanese_ci |      3 |
+----------+-----------------------------+---------------------+--------+
36 rows in set (0.00 sec)
A karakterkészlet ismerete azt jelenti, hogy egy szerverhez, adatbázishoz, táblához, oszlophoz hozzárendelhetünk egy karakterkészletet - ez az információ akkor kerülhet felhasználásra, amikor konvertálásra kerül a sor. Erről majd mindjárt bővebben, mert nem teljesen egyértelmű. A karakterkészlet információ azonban nem határozza meg azt, hogy hogyan is kell rendezni az adott szöveget, erre a collation, magyarul egybevetés táblázatok szolgálnak, melyek elég összetett rendezési szempontokat is rögzíthetnek. Hogy miért nem a karakterkészlet tartalmazza ezt az információt? Bár elsőre teljesen logikusnak tűnhet ez a feltevés, de ha egy kicsit körbenézünk német nyelvterületen, kiderül, hogy többfajta rendezés is adott lehet, például a németeknél három fajta is van. Hogy érthetőbb legyen pontosan milyen okokból, nézzük meg, mi a különbség!

Német rendezésből 3 féle létezik: a DIN-1, DIN-2 és az osztrák (a DIN a "Német Szabványügyi Hivatal" rövidítést takarja, vagyis ezek a rendezések szabványban rögzítettek). Mind a három szabálygyűjteményben az "ß" és az "ss" karaktersorozatok ugyanazzal a súllyal szerepelnek. A DIN-1 az ékezetes karaktereket az ékezetmentes változatukkal egyenrangúnak tekinti (pl.: "Ö"=="O"). Ez a szabály a szavak rendezésére lett kitalálva, ezt használják a szótárak, illetve a svájci telefonkönyv is. A DIN-2 az ékezetes karaktereket két betűnek veszi: az ékezetmentes változathoz plusz egy "E" betű csatlakozik (pl.: "Ö"=="OE"). Ez a szabály nevek rendezésére lett kitalálva, a német telefonkönyvek eszerint kerülnek rendezésre. Végül az osztrák jelölés egy harmadik utat választott, az ékezetes betűk egy kicsit nagyobb súllyal rendelkeznek, mint az ékezet nélküli változat. Az osztrák rendezést egyre ritkábban használják Ausztriában is, helyette valamelyik DIN szabványt alkalmazzák. A MySQL (hasonlóan más adatbázis szerverekhez) csak a két DIN szabványhoz ad kész megoldást.

A rendezések természetesen nem állnak meg a német rendezési megoldásoknál, a legtöbb nyelvnek saját sorrendje van, így a nyugat-európai karaktereket tartalmazó Latin1 karakterkészlethez a MySQL mindjárt 8 különböző rendezési lehetőséget is ismer, amit a következőképpen listázhatunk ki mi is:
mysql> show collation like 'latin1%';
+-------------------+---------+----+---------+----------+---------+
| Collation         | Charset | Id | Default | Compiled | Sortlen |
+-------------------+---------+----+---------+----------+---------+
| latin1_german1_ci | latin1  |  5 |         |          |       0 |
| latin1_swedish_ci | latin1  |  8 | Yes     | Yes      |       1 |
| latin1_danish_ci  | latin1  | 15 |         |          |       0 |
| latin1_german2_ci | latin1  | 31 |         | Yes      |       2 |
| latin1_bin        | latin1  | 47 |         | Yes      |       1 |
| latin1_general_ci | latin1  | 48 |         |          |       0 |
| latin1_general_cs | latin1  | 49 |         |          |       0 |
| latin1_spanish_ci | latin1  | 94 |         |          |       0 |
+-------------------+---------+----+---------+----------+---------+
8 rows in set (0.01 sec)
Minden karakterkészlethez tartozik egy alapértelmezett rendezés is, ahogy a fenti lekérdezésből is látszik, a Latin1 készletnél ez a svéd lett.

Az általunk használható Latin2 kódoláshoz is létezik ötfajta rendezés, köztük a magyarral "latin2_hungarian_ci" néven. Ha a szövegeinket inkább UTF-8 kódolással szeretnénk eltárolni (célszerű), nincsen konkrét magyar collation, mind az "utf8_general_ci", mind pedig az "utf8_unicode_ci" sorrendeket használhatjuk, helyes eredményt kapunk (vagyis: á, Á, é, É, í, Í, ó, ö, ő, Ó, Ö, Ő, ú, ü, ű, Ú, Ü, Ű).

A karakterkódolás beállítások

A MySQL adathierarchiában négy szinten tudunk kódolási/rendezési információt megadni. Globálisan az adatbázisszerverre, egy adott adatbázisra, egy adott táblára és egy adott oszlopra.

Az adatbázisszerver kódolása

Az adatbázisszerver karakter kódolása, illetve a rendezése nem lehet NULL érték. Az értékük csak és kizárólag adatbázistáblák létrehozásakor számít, semmilyen konverzióban, vagy más adatbázis elem létrehozásakor nem számítanak. Adatbázistáblák létrehozásakor is akkor lesznek figyelembe véve, ha explicite nem adtunk meg más kódolást a CREATE DATABASE parancs kiadásakor.

Értékük alapvetően a szerver indításkor kerül beállításra, illetve szabadon állítható a szerver futása során is. A szerver eleve egy adott karakterkészlet támogatással kerül lefordításra, ezt indítási paraméterekkel (például a my.cnf fájl segítségével) felül tudjuk bírálni. A my.cnf fájlban a következő módon tudjuk beállítani az induláskor érvényes kódolást:
[mysqld]
...
default-character-set = latin2
default-collation = latin2_hungarian_ci
...
Futás közben lekérdezni a beállításokat a következőképpen lehet:
mysql> show variables like '%_server';
+----------------------+---------------------+
| Variable_name        | Value               |
+----------------------+---------------------+
| character_set_server | latin2              |
| collation_server     | latin2_hungarian_ci |
+----------------------+---------------------+
2 rows in set (0.00 sec)
És végül megváltoztatni:
mysql> set character_set_server = 'utf8';
Query OK, 0 rows affected (0.00 sec)

mysql> set collation_server = 'utf8_unicode_ci';
Query OK, 0 rows affected (0.00 sec)
Bővebben a szerver karakterkészlettel kapcsolatos beállításairól: http://dev.mysql.com/doc/refman/5.0/en/charset-server.html

Adatbázisok kódolása

Mindegyik adatbázisnak van egy alapértelmezett karakter kódolása és rendezése (mely értelemszerűen szintén nem lehet NULL érték). Az adatbázis kódolás beállításának egyetlen szerepe, hogy alapértelmezett értéket nyújtson táblák létrehozásakor.

Egy adatbázisnak a létrehozásakor megadhatjuk a kódolását az alábbi szintakszissal:
CREATE DATABASE adatbázisnév
    [...]
    [[DEFAULT] CHARACTER SET karakterkészlet]
    [[DEFAULT] COLLATE rendezés]
Amennyiben ezeket az információkat elhagyjuk, az adatbázis az éppen aktuális szerver kódolással, rendezéssel jön létre. Ha nem adunk meg rendezést, de karakterkészletet igen, akkor az adott karakterkészlethez tartozó alapértelmezett rendezés kerül beállításra (és nem a szerver rendezése!).

Egy már létező adatbázis kódolását az ALTER parancs segítségével módosíthatjuk a következőképpen:
ALTER DATABASE adatbázisnév
    [[DEFAULT] CHARACTER SET karakterkészlet]
    [[DEFAULT] COLLATE rendezés
Az éppen kiválasztott (USE használatával) adatbázis kódolását és rendezését a következőképpen kérdezhetjük le:
mysql> show variables like '%_database';
+------------------------+-------------------+
| Variable_name          | Value             |
+------------------------+-------------------+
| character_set_database | latin2            |
| collation_database     | latin2_general_ci |
+------------------------+-------------------+
2 rows in set (0.00 sec)
Ha nincs kiválasztva adatbázis, akkor itt a szerver beállításokat fogjuk látni. Minden adatbázis váltáskor ezek az értékek átállítódnak.

Bővebben az adatbázisok karakterkészlettel kapcsolatos beállításairól: http://dev.mysql.com/doc/refman/5.0/en/charset-database.html

Táblák és oszlopok kódolása

Mindegyik adatbázis táblának van egy alapértelmezett karakter kódolása és rendezése (nem lehetnek NULL értékűek). A tábla létrehozás művelet során nem csak a tábla, hanem az oszlopai is létrejönnek (nyilván), ezért bár egy kicst másképp látszik viselkedni, egyetlen szerepe a karakter típusú (CHAR, VARCHAR, TEXT) oszlopaihoz tartozó kódolás és rendezés alapértelmezett beállítása. Ezen kívül semmilyen hatással nincsen a táblázat viselkedésére, illetve annak tartalmára.

Egy tábla létrehozásakor a következőképpen adhatjuk meg az alapértelmezett kódolását, rendezését:
CREATE TABLE táblanév (oszlopok definíciója)
    [DEFAULT CHARACTER SET karakterkészlet [COLLATE rendezés]]
Ha nem adunk meg karakterkészletet, akkor az adatbázis számára beállított érték lesz érvényben, ha megadunk karakterkészletet, de rendezést nem, akkor az adott karakterkészlet rendezése fog hozzárendelődni a táblához.

A tábla létrehozása során, ha egy adott oszlopnál nem definiálunk külön karakterkészletet illetve rendezést, akkor a táblának beállított karakterkészlettel jönnek létre a karakteres oszlopok. Egy oszlop karakterkészlete és rendezése minden további nélkül eltérhet azonban a táblának beállított alapértelmezett értékektől, erre nézzünk is pár példát!

Egyes számú példa, nem adunk meg semmit sem:
CREATE TABLE pelda (id int, txt text);
Ekkor az adott adatbázis beállításai lépnek érvényre, és mind a tábla karakterkészlete és rendezése, mind pedig a txt oszlop karakterkészlete és rendezése onnan fog jönni.

Kettes számú példa, csak karakterkészletet adunk meg:
CREATE TABLE pelda (id int, txt text)
    DEFAULT CHARACTER SET latin2;
Ekkor mind a tábla karakterkészlete, mind pedig a txt oszlop karakterkészlete Latin2, a rendezés pedig a Latin2 karakterkészlethez tartozó alapértelmezett rendezés lesz.

Hármas számú példa, mind karakterkészletet, mind rendezést megadunk:
CREATE TABLE pelda (id int, txt text)
    DEFAULT CHARACTER SET latin2 COLLATE latin2_hungarian_ci;
Ekkor a tábla és a txt oszlop karakterkészlete Latin2, a rendezés pedig magyar lesz.

Négyes számú példa, a txt oszlopra külön állítunk be karakterkészletet:
CREATE TABLE pelda (id int, txt text CHARACTER SET latin2);
Ekkor a tábla karakterkészlete és rendezése az adatbázisának az alapértelmezettje lesz, a txt oszlop karakterkészlete Latin2, rendezése pedig a Latin2 alapértelmezett rendezése lesz.

Ötös számú példa, a txt oszlopra külön állítunk be karakterkészletet és rendezést is, és a táblázatnak is adunk egy alapértelmezett karakterkészletet, rendezést:
CREATE TABLE pelda (
    id int,
    txt text CHARACTER SET latin2
             COLLATE latin2_hungarian_ci
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Ekkor mind a txt oszlop, mind a tábla úgy lesz beállítva, ahogy megadtuk. Ebben az esetben is igaz: a tábla beállítása nincsen hatással semmire sem, csak akkor lesz figyelembe véve, ha egy új oszlopot szeretnénk hozzáadni a táblához.

Mind a tábla, mind pedig egyes oszlopai karakter kódolását, rendezését módosíthatjuk bármikor az ALTER parancs segítségével. A szintakszis a tábla esetén a következő:
ALTER TABLE táblanév
    DEFAULT CHARACTER SET karakterkészlet [COLLATE rendezés]
Ennek hatására semmi sem fog változni a működést illetően, az adataink sem lesznek átkonvertálva.

Egy oszlop karakter kódolását, rendezését a következőképpen változtathatjuk meg:
ALTER TABLE táblanév
    CHANGE oszlopnév oszlopnév típus
    CHARACTER SET karakterkészlet [COLLATE rendezés];
Ennek hatására az adott oszlop értéke konverzión esik át, melynek eredménye adatvesztés is lehet: például ha Latin2 karakterkészletű oszlopot kódolunk át Latin1-re, akkor az ő és ű karaktereink helyén szép kérdőjelek maradnak csak, mivel ezek a karakterek nem szerepelnek a Latin1 kódkészletben.

Ész nélkül ne konvertáljuk adatainkat! Ha eddig valamilyen okból Latin1 kódolással voltak letárolva adataink (de mi Latin2-ként kezeltük), és most azt szeretnénk, hogy a MySQL helyesen Latin2 kódolásúnak ismerje, elveszhetnek az ő, ű és Ő, Ű karakterek, mivel a Latin1 kódolásban ezek helyén lévő karaktereknek nincs megfelelője a Latin2 kódolásban. Egy adatbázis dump, majd import segíthet a dolgon talán a legkönnyebben. Csináljunk biztonsági másolatot bármilyen rizikós művelet előtt (és amúgy is)!


Ha átkonvertáljuk adatainkat például Latin2-ről UTF-8-ra, majd egy lekérdezéssel ellenőrizni szeretnénk, hogy sikerült-e a konverzió, s arra számítunk, hogy mindenféle két bájtos krix-kraxokat kapunk vissza, tévedni fogunk, mivel a MySQL automatikus konverziót is végez lekérdezéskor, ezáltal láthatatlanná téve számunkra az átalakítást. Hogy mégis történt átalakítás, onnan tudhatjuk meg, hogy lekérdezzük a konvertált oszlopok hosszát, s látni fogjuk, hogy az ékezetes karakterek helyfoglalása immáron két bájtra nőtt.

További információkat a táblák és oszlopok karakterkészletéről, rendezéséről a következő címeken találhatunk: http://dev.mysql.com/doc/refman/5.0/en/charset-table.html, http://dev.mysql.com/doc/refman/5.0/en/charset-column.html.

Most, hogy megismertük a karakterkészletek rögzítési lehetőségeit, térjünk át az automatikus konverzió témakörére, s lássuk, mit és hogyan alakít át számunkra transzparensen a MySQL!

A MySQL automatikus konverziói

A MySQL a karakterkészlet információk alapján a klienshez igazodva automatikus konverziókat végez, azaz például képes a Latin2-ben küldött adatainkat az UTF-8 kódolású táblába beszúrni megfelelő kódolással, illetve lekérdezéskor visszafele elvégezni a konverziót, úgy, hogy a visszakapott érték megint Latin2 legyen. Bár megvalósításánál fogva a konverzió nagyon gyors, de mégis időt vesz igénybe, ezért hogy ha igen-igen magas a MySQL terhelése, érdemes figyelni rá, hogy ne kelljen konvertálnia.

Az automatikus konverzióért négy változó felelős:
  • character_set_client, ez azt mondja meg, hogy a kliens által elküldött bájtsorozat milyen kódolású;
  • character_set_connection és collation_connection, ezek azt határozzák meg, hogy a szerveren milyen kódolású legyen az elküldött kérés;
  • character_set_results, ez pedig a szervertől visszakapott adatok kódolását határozza meg.


Ezeknek a változóknak az értelmezésében szintén nem segített sokat a MySQL dokumentáció, illetve bár teljesen egyértelmű, hogy mire szolgál a character_set_client és a character_set_results, a character_set_connection értelmezése feladta számomra a leckét. A probléma az volt, hogy elvileg teljesen egyértelműnek kell lennie, hogy mire konvertáljon a MySQL - ha beszúrok egy mezőt, akkor annak az oszlopára kell konvertálni, ha összehasonlítok egy sztringet egy értékkel, akkor az érték kódolása a meghatározó stb.. A MySQL azonban nem így gondolkodik, az SQL lekérdezések elküldése után egyből végrehajt egy konverziót, majd ez alapján kezeli az elküldött sztringet. Ezután ha kell, erről a karakterkészletről konvertál, ez alapján hasonlít össze stb..

Ezeket az értékeket általában nem nekünk kell állítanunk, mivel a kliensprogram elvégzi helyettünk a beállításukat - a kliensprogramtól függő logikával. Jellemzően mind a három karakterkészlet értéket ugyanarra állítja, arra a kódolásra, melyről úgy gondolja, hogy számára a legjobb. A mysql kliensprogram például a konzol kódolása alapján állítja be ezt az értéket, az egyes programozási nyelvek azonban más megfontolásokat is alkalmazhatnak.

Jellemzően mi is akkor járunk jól, ha ezeket az értékeket ugyanarra állítjuk, azaz nem történik az adatátvitel során konverzió. A három érték egyszerre történő átállítására a MySQL kínál is egy egy parancsos lehetőséget, a SET NAMES karakterkészlet parancsot (például: SET NAMES utf8). Ettől eltérni talán akkor van értelme, ha a kliens oldalon más a kódolásunk, mint a szerver oldalon (például latin2 van kliens oldalon, de az adatbázis utf8-ban tárolja az adatokat), s ez darabra túl sok automatikus konverziót eredményezne. Ekkor a character_set_connection szerver oldali kódolásra történő átállításával egyszer lesz csak konverzió, mivel már feltöltés után egyből átalakításra kerül az adat.

Bővebben az automatikus konverzióról: http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html

Karakter kódolás jelölése

Előfordulhat, hogy egy a lekérdezésben szereplő sztringnél jelezni szeretnénk, hogy milyen kódolásban szerepel, hogy amikor a MySQL konvertálja, helyes értéket hozzon ki végeredményként. Ehhez a sztring elé kell írnunk az adott karakterkészlet nevét egy aláhúzással egyetemben, ezáltal a MySQL megfelelően fogja kezelni:
INSERT INTO utf8tábla (utf8oszlop) VALUES (_latin2'Árvíztűrő tükörfúrógép')
Ha a céloszlop UTF-8 kódolású, a kapcsolatunk is UTF-8 kódolású, de a sztringünk Latin2-ben lesz elküldve, akkor ezzel a jelzéssel konvertálhatjuk.

Hasonló lehetőség függvényként is rendelkezésünkre áll, CONVERT() a neve. Ennek szintakszisa a következő:
INSERT INTO utf8tábla (utf8oszlop) VALUES (convert('Árvíztűrő tükörfúrógép' USING latin1))
Ezekre a függvényekre valószínűleg ritkán lesz szükségünk, illetve jobb, ha másképp oldjuk meg az ilyen jellegű konverziót, de lehet, hogy bizonyos körülmények között mégis így a legegyszerűbb a konverzió.

Összefoglalás

A fenti összefoglaló talán segítséget nyújthat a MySQL karakterkészletekkel kapcsolatos lehetőségeinek megismerésében. Számomra a MySQL jelenlegi dokumentációjából hiányzott, hogy pontosan mikor és hogyan alakulnak át az adataim, illetve melyik beállításnak milyen hatása van, a háttérben pontosan mi történik: ezekre a kérdéseimre összegyűjtve a válaszokat született meg ez a cikk. A MySQL lehetőségeit ezen a téren elég kellemesnek találom, manapság még elég sok esetben fordul elő, hogy vegyes kódolású környezetben kell dolgoznunk, az egyik projekt így, a másik projekt úgy kezeli a karaktereket. A MySQL karakterkészleteivel megkönnyíti a migrációt, illetve lehetőséget kínál arra is, hogy egyik projektjünkkel átálljunk egy másik karakterkészletre úgy, hogy egy másik projektünk ne vegye észre a váltást, akkor sem, ha ugyanahhoz az adatbázishoz próbál csatlakozni más karakterkészletet feltételezve.

Bár a cikk a MySQL-ről szól, de ha valamely olvasónak van tapasztalata más adatbázisok karakterkészlet kezeléséről (nekem sajnos nincs), szívesen venném, ha egy-két bekezdésben (vagy ha van kedve, egy cikkben) összefoglalná, hogy az adott adatbáziskezelő milyen lehetőségeket kínál ezen a téren, miben jobb, esetleg miben rosszabb a MySQL-nél.
 
Bártházi András arcképe
Bártházi András
Az Emarsys-nál dolgozik vezető fejlesztőként, és az API-ért, integrációkért felelős termékmenedzserként. Szeret profi csapatban profit alkotni.
1

Köszönjük!

aries · 2005. Nov. 21. (H), 12.22
András!

Köszönjük, hogy a vasárnapodat azzal töltötted, hogy megírd ezt a nagyon jó cikket!
--
Aries
http://aries.mindworks.hu
2

Szuper!

EdgarPE · 2005. Nov. 21. (H), 14.15
Pont egy ilyen leírásra volt most szükségem, szóval kösz!

Néhány elgépelés:
A cikk második felében jónéhány helyen össze van keverve a Latin1 és Latin2 (mindenhol Latin1 van, ott is ahol Latin2-nek kellene lenni).
Bár a szövegkörnyezetből egyértelmű hogy mi akar ott lenni, ugyhogy nem túl zavaró.
4

Nem elgépelés, rossz csere :)

Hodicska Gergely · 2005. Nov. 21. (H), 17.49
Ezt sajnos én keféltem el, nem András. Nagybetűsítés során volt egy rossz csere ezek szerint. Köszi szépen a jelzést, javítva.


Felhő

u.i. Erre mondják, hogy pokol fele vezető út csupa jószándékkal van kikövezve.
3

^o vs ~o

Őry Máté · 2005. Nov. 21. (H), 17.17
hello
szep, hasznos cikk
en ugy tudtam, hogy a latin2 fele ooe karakter helyen a latin1ben a tlideos o van, nem a kalapos. eliras, vagy en tudom rosszul?
az uue helyen van kalapos u.
6

Jól tudod

Bártházi András · 2005. Nov. 21. (H), 20.59
Jól tudod, de én már csak így hívom. :)

-boogie-
8

Sőt...

Bártházi András · 2005. Nov. 22. (K), 00.22
Még annyit hozzátennék, hogy a kalapos ő pedig mind a Latin1, mind a Latin2 része, legalábbis eszerint: http://czyborra.com/charsets/iso8859.html Úgyhogy javítom.

-boogie-
5

<Nincs cím>

Anonymous · 2005. Nov. 21. (H), 20.33
Ez tényleg hasznos cikk. Nemrég addig konvertálgattam értelmetlenül az adatbázisaimat, míg megtaláltam a jó megoldást. Akkor levontam pár következtetést, de így már minden sokkal világosabb. Köszi!

üdv,
Feri
7

Unicode

Bártházi András · 2005. Nov. 21. (H), 23.46
A unicode kapcsán kaptam egy kritikát levélben (ezúton köszönöm), ezért egy kicsit érthetőbben és remélhetőleg a valósághoz még jobban közelítően :) kibővítettem egy kicsit az adott részt. Ami biztos, hogy sokak fejében (az enyémben is) téveszmék terjengenek arról, hogy a Unicode 16, 21, 32 vagy mennyi bites. A rendelkezésemre álló források alapján most talán a legjobb leírást lehet fentebb olvasni.

-boogie-
9

"Karakterkészletek, kódolások" részhez

Hodicska Gergely · 2005. Nov. 22. (K), 01.26
Egy dolgot szeretnék ezzel kapcsolatban kihangsúlyzoni (amire András utalt is egy érzékletes példával): egy szöveg önmagában nem értelmezhető, csak a karakterkódolásának tudatában, enélkül csak egyszerű byte-ok sorozata, melyek "bármit" is jelenthetnek.


Felhő
10

Csak egy megjegyzés

Balogh Tibor · 2005. Nov. 22. (K), 02.06
Ez ad lehetőséget arra, hogy pl. unicode karakterkészletet nem támogató adatbázisban is tároljunk unicode szöveget. Persze itt a unicode csak mint példa szerepel, bármilyen egyéb, nem támogatott karakterkészletnél ez a helyzet.

Ekkor a tárolt szöveg csak egy adatfolyam, mondjuk egyébként is. Ha nincs nekünk megfelelő karakterkészlet támogatás, akkor le kell mondanunk a megfelelő rendezésről, és esetleg szembe kell néznünk azzal, hogy a DBMS egyes utasítások futtatása alatt "kijavítja" az adatainkat. Ez utóbbi valószínűleg nem fog előfordúlni, ha binary típusú mezőben tároljuk a bájtsorozatot.
11

collate

Anonymous · 2005. Nov. 22. (K), 18.29
Jó cikk, köszi! Tényleg elég bonyolult (picit talán túlkomplikált) a mysql rendszere, ahogy agyba-főbe alakítgat, de ez a leírás segíthet kiigazodni.

Igazán jelentéktelen apróság: a Unicode nem 32, hanem 31 bites (kb. 2 milliárd), míg az említett 21 bit, amit valószínűleg nem fognak túllépni, az nem 1 millió, hanem 2 millió körül van.

Az utf8-ban ábrázolt karakter maximális hossza 3, 4, vagy 6. Attól függ. A Basic Multilingual Plane (első 65536 karakter) esetén maximum 3 byte. A tervezett 2^21 szimbólum esetén maximum 4 byte, míg elvileg ha mind a 2^31 értéket támogatni akarja valaki, akkor 6 byte. A karakterkészletek táblázatában az utf8 sor és Maxlen oszlop találkozásában 3-as szám szerepel, ez alapján arra gyanakszom, hogy a MySql csak a Basic Multilingual Plane-t támogatja.

Ami a rendezéseket illeti:

A magyar nyelv ábécébe rendezése nem túl egyszerű. Aa magánhangzók páronként (a-á, e-é, i-í, o-ó, ö-ő, u-ú, ü-ű) azonosnak tekintendők, kivéve ha semmi más különbség nincs a két szó között. A dupla (tripla) mássalhangzók (cs, dz, dzs, gy, ly, ny, sz, ty, zs, de a régies kettősbetűk nem) önálló "tokent" alkotnak, melyek az önállóan álló első betűjük után következnek, tehát például cz < cs (mert a cs egy külön egység, míg a cz az simán c és z egymás után, és a cs a c után jön). A hosszú kettős mássalhangzók (pl. ssz) úgy tekintendők, mint két sz token egymás után. Itt persze már a szó értelmével is foglalkozni kell, mivel egy "ssz" lehet akár két "sz", akár egy s és egy sz betű egymásutánja is (találós kérdés: a karosszéria szóban melyik van?) (tehát meggyőz < meggyfa, mert előbbi m-e-g, utóbbi pedig m-e-gy kezdetű, na szoftver legyen a talpán amelyik ezt helyesen felismeri), és hasonlóan például az "szs" karaktersorozat dekódolása sem egyértelmű. Továbbá nem találtam szabályt arra vonatkozóan, hogy a magyar ábécében nem szereplő betűk (például tulajdonnévben francia ékezet) hogyan rendezendők sorba.

Nem tudom, hogy a latin2_hungarian_ci mennyit implementál a fentiekből. Valaki meg tudja mondani? Viszont roppant fontos lenne, hogy a fenti szabályokból minél többet implementáló magyar rendezés ne csak latin2, hanem utf8 karakterkészlet esetén is elérhető legyen. A glibc a fentieket egész jól implementálja a magyar nyelvre latin2 és utf8 karakterkészletre egyaránt. A MySql nem képes a libc ezen adatbázisát használni, mindenképpen saját maga szeretné kivitelezni a rendezéseket? Ha igen, akkor az szerintem nem túl jó. Mindenképpen kellene utf8_hungarian_ci is.

-- egmont
12

Nem magyar betűk rendezése

Anonymous · 2005. Nov. 30. (Sze), 13.41
"Továbbá nem találtam szabályt arra vonatkozóan, hogy a magyar ábécében nem szereplő betűk (például tulajdonnévben francia ékezet) hogyan rendezendők sorba."

A könyvtárak erre a transzliterációt használják általában:

http://hu.wikipedia.org/wiki/%C3%81t%C3%ADr%C3%A1s_(nyelv%C3%A9szet)

http://mek.oszk.hu/html/irattar/ajanlas/pulman/tobbnyelvuseg.html#betuszerint
14

OFF: karosszéria

kutyamutya · 2008. Okt. 13. (H), 12.51
Legjobb tudomásom szerint a a "karosszéria" szavunk a francia "carosserie" szóból eredeztethetõ, szóval a kocsiban nem egy "karos széria" van, hanem egy karosszéria :)
Üdv

PS: és közben lett utf8_hungarian_ci is...
13

Latin1 -> Latin2

Anonymous · 2005. Dec. 25. (V), 22.12
Mivel arrol nem irtal, hogy hogyan lehet Latin1-rol atallni Latin2-re ezert levesem ide:

su
root@szzoltan:~#
root@szzoltan:~# mysqldump -p --default-character-set=latin1 --databases 
AZ_EN_ADATBAZISOM > teszt.sql
Enter password:
root@szzoltan:~#
root@szzoltan:~# mysql -p AZ_EN_ADATBAZISOM < /root/teszt.sql
Enter password:
root@szzoltan:~#mcedit /etc/my.cnf
#irjuk bele ezt a 3 sort
[mysqld]
default-character-set = latin2
default-collation = latin2_hungarian_ci
root@szzoltan:~#service mysqld restart
Nalam ez igy hibatlanul mukodott annyi kis buggal, hogy a COLLATE latin2_general_ci lett. Persze ezt mar egy alter-ral helyre rakhato kodolas nelkul. Innen pedig csak egy lepes az utf8.
bye Oregon
15

Köszönet és kieg.

Porrima · 2015. Feb. 18. (Sze), 19.47
A jó cikk, leírás haszna nem függ az időtől ;-)
Én éppen a napokban vettem nagy hasznát ennek a leírásnak.
Azért szólok hozzá, mert jelenleg már az UTF-8 készletnek is van jó néhány illesztése, többek között hungarian_ci is. Azért érdekes, hogy a magyar sorrend nem azonos az elképzelésemmel, azaz nem tesz különbséget a rövid és hosszú magánhangzók között. Ez o és ó stb. esetében nem is annyira zavaró, de az á betű sokkal jobb lenne, ha az a után következne, szerintem.
16

mek.oszk.hu - helyesírási

pythonozok · 2015. Feb. 18. (Sze), 20.07
mek.oszk.hu - helyesírási szabályzat. Én még úgy tanultam, hogy van különbség az ékezetes betűk közt, ha ABC sorrendről beszélünk, a szabályzat szerint nincs.
17

A betűrendbe szedés magyar

MadBence · 2015. Feb. 18. (Sze), 21.07
A betűrendbe szedés magyar szavak esetén nagyon bonyolult, szerintem algoritmikusan (szólista nélkül) nem is lehet megcsinálni (figyelembe kell venni a kétjegyű (háromjegyű) mássalhangzókat, amik meg kontextusfüggőek). Maradj a közelítő megoldásoknál (amit az SQL is ad).