Datový typ boolean v MySQL

cs in code

Už je to dlouho, co jsem se s vámi chtěl podělit o tip, jak vytvořit datový typ boolean v databázi MySQL trochu jinak. Nějak jsem na to úplně zapomněl, takže se s ním dělím dnes.

V MySQL datový typ boolean je, ale není to samostatný datový typ, nýbrž alias pro TINYINT(1). V normálních situacích by to mělo stačit, ale občas nastane prostě specifická situace, kdy to nepostačuje…

Dělám totiž na business aplikaci, kde je velmi často co nějaký formulářový prvek, to atribut v databázi. Každý takový formulářový prvek je nějak omezen a uživatele musíme případně upozornit, pokud takové hranice překročí. Programátor je od přírody tvorem lenivým a proto nic nepíše dvakrát (tzv. DRY) a tudíž ani my jsme nechtěli validace psát vícekrát.

„Vícekrát?“ ptáte se? Ano, vícekrát. Nejenom, že musím někde v kódu specifikovat validační pravidla, ale i databázi musím nějak nastavit. Už jenom tohle je dvakrát a to nepočítám nějaké menší kontroly na straně klienta – v JavaScriptu. Například věk. V databázi musím určit TINYINT a v kódu ošetřit vstup, aby mi minimálně databáze neodpověděla out of range.

Abychom to nemuseli dělat, napsali jsme si takovou fičuru – při nastartování aplikace se přečte celá databáze a pro každý atribut v každé tabulce se sestaví validační pravidlo. Jaký to je datový typ a jaká jsou omezení (maximum/minimum, maximální/minimální délka, možnost nastavit NULL, …). A tyto pravidla se využijí při validaci (která si samozřejmě můžeme v aplikaci ještě zpřísnit).

Tedy když pomocí SQL syntaxe řeknu, že e-mail může být maximálně 256 znaků dlouhý, nemusím toto stejné číslo použít znovu někde v kódu. A kdyby se náhodou toto omezení změnilo, stačí provést úpravu na jednom místě.

Řešení to je hezké a funguje nám to bez problémů dlouho (něco přes rok), ale měli jsme s tím při programování menší problém. Jednalo se o datový typ boolean. V databázi jsme ho měli jako každý jiný vytvořen pomocí TINYINT(1), jenže takto tam máme i atributy, které jsou skutečně celočíselné – nabývající od nuly do devíti.

Člověk je rozliší poměrně jednoduše podle názvu, ale robot generující validační pravidla to má o něco horší. Mohli jsme mu to usnadnit například nějakým speciálním zápisem do poznámky, ale to není úplně to pravé ořechové. Poznámka / komentář je jen poznámka / komentář. Něco takového by nemělo být rozhodujícím pro běh programu. To jsme tedy neudělali.

Udělali jsme to, že TINYINT(1) jsme převedli (kde se jednalo o logickou hodnotu) na BIT(1). Dřív byl BIT také alias pro TINYINT, ale od MySQL 5.0.3 je to samostatný datový typ, takže ho lze rozlišit a dokonce může správně nabývat pouze dvou hodnot.

Výhody to nemá – datově si nijak nepolepšíme (BIT(1) zabírá totéž jako BIT(2) nebo i BIT(8)) a nepolepšíme si ani nijak jinak. Vlastně si ještě ztížíme práci, protože BIT se přenáší jako BINARY, což je v MySQL řetězec. Na to je jednoduchá náprava: stačí hodnotu převádět například pomocí funkce CONVERT.

MySQL dokumentace radí i převádění pomocí přičtení nuly, ale to mi nepřijde zrovna přehledné. Ale je to kratší když pracujete v konzoli. ;)

Jinak žádné další problémy s použitím BITu jsme neměli. Dokonce je teď jasnější, že se jedná o logickou hodnotu, protože TINYINT(1) prostě neříká „hej, čau, já jsem nějaký flag!“ Což potvrzuje to, že máme skutečně atributy TINYINT(1), které nejsou logické.

Pokud tedy někdy přestane vyhovovat TINYINT(1) jako logická hodnota i vám a v MySQL stále nebude (stále není v poslední MySQL 5.6) pravý BOOLEAN, zkuste náhradní řešení s BIT(1).

Pro vysoký výkon se hodí naopak něco jiného: CHAR(0) DEFAULT NULL (v případe InnoDB). Tohle řešení se mi mi zdá velmi ugly, ale co bychom neudělali pro vysoký výkon, že? :) O tom už ale jinde, například v knize High Performance MySQL nebo v článku Efficient Boolean value storage for Innodb Tables.





You may also like