Datový typ boolean v MySQL

cs v kategorii code • 4 min. čtení
Mind the age! Most likely, its content is outdated. Especially if it’s technical.

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 tato 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.






5 reakcí

No tak ono by v konečném důsledku pro fungování vaší "fičury" stačilo označit datový typ jako SET('0','1'), teďka nevím, jestli se to vleze do bajtu, nebo to spolkne bajty dva, ale bude to fungovat.

Spíš nechápu tu strategii validovat na základě struktury databáze - osobně všechno stavím tím stylem, že od uživatele do SQL vede hodně dlouhá cesta a prakticky veškeré validační procesy řeší jedna vrstva někde v půli téhle cesty,,,

Když už, tak ENUM (logická hodnota nemůže být záporná a zároveň kladná). Dále se stejně nezbavíš převodu na číslo a v databázi to zabírá podstatně více místa. Navíc BIT(1) vypadá více jako logická hodnota.

U nás je také dlouhá cesta od uživatele k databázi. JavaScript (ten teda u nás zatím moc velkou roli nemá), webovka, server a pak až databáze. Ale to neznamená, že se například server nemůže při nastartování zeptat databáze, jaké má omezení třeba pro ten e-mail – zda je povinný, jak může být maximálně dlouhý atp. Samozřejmě to je jen pomůcka, abychom nemuseli psát všechno ručně a několikrát.

SET a ENUM si pletu, přiznávám (v případech, kdy by se daly použít téměř vždycky najdu strukturálně pružnější řešení, většinou to skončí IDčkem a pomocnou tabulkou). BIT(1) pro tenhle úkol zní sice fajn, ale tam mám závažný problém kvůli té nutné konverzi (pro změnu specifikum našeho systému, kde je tohle příliš složité, na druhou stranu, s tím TINYINT(1) absolutně problém nemáme).

Obecně tu fixaci na strukturu databáze nemám moc rád, používáme i jiné způsoby ukládání dat a často provádíme vzájemné konverze, čili bychom si tím stejně dvakrát nepomohli, což nic nemění na tom, že, vzhledem k dost omezeným možnostem definice typu (a ten chybějící BOOLEAN je toho zářným příkladem) to stejně nepovažuji za systémové řešení...

Jak už je v článku – je to pro business aplikaci, kde nám to velice pomohlo. Určitě to nelze použít kdekoliv. V článku jsem to nijak více nerozebíral, protože to je uvedené jen jako důvod, proč nám nevyhovoval TINYINT(1) a použili jsme BIT(1). Ani neříkám, že je nutné BIT(1) použít. Jen říkám, že se osvědčil: je to (minimálně stejně) efektivní a jasnější. Mimochodem konverze je nutná jen při SELECTu, stejně jako vaše řešení pomocí ENUM.

Ačkoliv Vaše důvody zcela chápu, obecně se mi zdá lepší i to řešení v ENUM - z dlouhodobého hlediska je s binárními typy v MySQL problém ohledně importu a exportu - v první fázi to zjednoduší práci, později to vytváří neřešitelné problémy, které si vyžadují strukturální zásahy...





Může se vám také líbit

en Makefile with Python, November 6, 2017
en Fast JSON Schema for Python, October 1, 2018
en Deployment of Python Apps, August 15, 2018
cs Jasně, umím Git…, August 6, 2014
cs Checklist na zabezpečení webových aplikací, March 1, 2016

Další články z kategorie code.
Nenechte si ujít nové články díky Atom/RSS kanálu.



Poslední příspěvky

cs Mami, tati, přejde to, December 9, 2023 in family
cs Co vše bychom měli dělat s dětmi?, November 24, 2023 in family
cs O trávicí trubici, November 7, 2023 in family
cs Na šestinedělí se nevyspíš, October 28, 2023 in family
cs Copak to bude?, October 20, 2023 in family