Portál AbcLinuxu, 30. dubna 2025 09:05
Tiskni
Sdílej:
Hlavně největší síla relačních databází je v systémech s hodně požadavky na čtení a málo požadavky na zápis (prakticky všechny databázové systémy co si člověk představí).
Tohle se spíš uvádí jako výhoda LDAP serverů (často ve srovnání právě s relačními databázemi). Samozřejmě jde hlavně o to, jak se konkrétní systém nastaví (nemluvě o tom, že ty LDAPy pod sebou taky někdy mají relační databázi).
tzv. big data všichni pořád zapisují, ale čtou se jenom jednou za čas, když je někdo analyzuje
A souvisí tohle až tak s relačním uspořádáním dat? Podle mého je to spíš o spolehlivost – jestliže chci mít transakce a chci mít jistotu, že vložená data se neztratí, tak je potřeba počkat, až se zapíší na disk a pak teprve potvrdit jejich vložení. A tady je úplně jedno, jestli ta data jsou uspořádána relačně, nebo objektově nebo třeba jako XML – ten kus dat prostě musí prolézt skrze systém a zapsat se fyzicky na disk – takže to limituje rychlost disku resp. IOPS.
Když oželíme spolehlivost (zapsaná data budou chvíli jen v RAM, kde se můžou v případě výpadku ztratit) nebo použijeme asynchronní zápis (data se uloží nejdřív do fronty, která není indexovaná a může být rozložená přes hodně disků), tak můžeme mít rychlé zápisy i nad tou relační databází.
Nechci ti kazit radost, zvlášť když je to tvůj první zápisek tady (jsem rád, že přibývají aktivní uživatelé), ale mám pár převážně kritických připomínek.
Uplně v první fázi to byl opravdu klasický model relační databáze. Tabulky, SQL a všechno, ale už od začátku se mi to nelíbilo, tak jsem všechno smazal, zavřel to a začal přemýšlet znova a hlavně nad tím, co mi vadilo na tom kódu co už jsem měl. Ten kód byl v pohodě a na mě i celkem čistý. Kód mi nevadil, vadilo mi to, že se to zvrhlo v relační databázi.
Pokud možno bych nepsal kód, dokud nemáš jasnou vizi, co to má být, návrh. Vím, že je to těžké, programátor rád píše kód + může být efektivní si připravit něco předem, i když návrh ještě není hotový. Většinou to řeším tak, že píšu kousky komponent nebo bloky kódu, o kterých vím, že tam budou – ale ne nic, co by souviselo s návrhem celého systému, to by byla zbytečná práce, když ještě není jasné, jak ten systém má vypadat. Příklad: vím, že v aplikaci budu potřebovat šifrování, tak si vyzkouším, jak se v daném jazyce/platformě šifruje, tím uspokojí svoji potřebu napsat nějaký kód, ale zároveň nepíšu nic, co bych pak zahodil nebo musel totálně předělat.
Co zahrnuje vytvoření dobré databáze tu popisovat nebudu, protože od toho tu nejsme, jen v rychlosti shrnu všechny kroky. Návrh databáze, jak mnozí vědí, zahrnuje vytvoření reality, návrh relačního schématu, vytvoření podle něj E-R diagramu a pak samotné vytvoření databáze, což zahrnuje víc kroků než jen prosté CREATE DATABASE. Je jedno jak velká je ta databáze, touhle cestou musím projít pokaždé, když něco databázuju.
Existuje tzv. Princip tří architektur (P3A), postupuje se od nejabstraktnějších modelů ke konkrétním:
Konceptuální je úplně nejobecnější, nejabstraktnější. Až v té technologické se rozhodneš, zda použít např. relační model nebo objektovou databázi atd. A v té implementační teprve vybereš (případně navrhneš vlastní vývoj) konkrétní software (např. PostgreSQL, eXist).
Tu realitu nevytváříš, ale jen modeluješ – vytváříš nějaký koncept, model, který jí bude přiměřeně přesně a přiměřeně kompletně odpovídat.
Značně frustrující je potom cokoliv měnit
Se změnami bys měl počítat od samého začátku – vývoj softwaru nekončí tím, že vydáš první verzi 1. Nauč se dělat inkrementy, nauč se testovat…
přidání cizího klíče pak může všechno rozbít a já tak utvrzuju zažité klišé, že databázisti jsou nerváci. Minimálně já při tom dost nervní jsem.
Návrh databáze je čistá radost, setkání s dokonalostí. Na rozdíl od jiných činností ve vývoji, kde musíš bojovat s různě nevyzrálými technologiemi/frameworky/knihovnami obsahujícími chyby a různé zákeřnosti.
Jestli jsi nervní, tak patrně proto, že jsi v minulosti udělal špatný návrh a teď to musíš předělávat. Zaměř se na normální formy a jednoduchost, v databázi měj jen čistá data a jen ta, která skutečně potřebuješ. Pak nebudeš mít takové nervy se změnami.
rychlé čtení z databáze
S tímhle relační databáze nemají problém. Stačí správně navrhnout model, nastavit indexy, případně použít materializované pohledy nebo si jinak předem napočítat data, která budeš potřebovat.
není nutný rychlý zápis, ať si dá klidně na čas
Viz výše – během zápisu si můžeš data převést do vhodné struktury pro čtení. Resp. uložit si je jak normalizovaná, tak vedle ještě jednou denormalizovaná pro rychlé čtení. Navíc můžeš mít i rychlý zápis (resp. jeho potvrzení a bezpečné uložení na disk), pokud ti nevadí, že se změny projeví asynchronně – zápisy můžeš rychle řadit do fronty (která bude ovšem transakční a bezpečně uložená na disk) a v jiném procesu je teprve denormalizovat a převádět na tvar vhodný pro čtení. To všechno nad relační databází.
single table, protože to chci co nejjednodušší
Tím ale jen přesuneš tu složitost do aplikace, která má s takovou databází pracovat.
To je ostatně jeden z důvodů vzniku DBMS – aby ta společná složitost byla v databázovém systému (kterých je jen pár) a nemusela být v každé aplikaci (kterých jsou spousty). Znovupoužitelnost.
mohutnost … zbytečně rozsáhlý backend kvůli prkotině, to možná souvisí s tou mohutností
Toho kódu u dnešních (R)DBMS je opravdu celkem hodně, to si člověk jen tak nepřečte (leda sem tam nějakou část, která ho zajímá). Na druhou stranu co se týče rychlosti startu a náročnosti při běhu: i tak robustní systémy se spoustou funkcí jako je PostgreSQL běží velmi rychle a jsou celkem nenáročné.
SQL
Nějaké konkrétní výhrady?
Jak už jsem řekl, data budou uložena do hashovací tabulky perfektním hashováním, protože přepočet kolizí stojí čas a čas je to o co se tu hraje.
Primitivních nerelačních databází/úložišť pro struktury typu klíč/hodnota jsou spousty. Měl by sis udělat nějakou analýzu. Např. si spočítej, kolik řádků kódu tyhle systémy obsahují a za jak dlouho bys to byl schopný napsat ty. A o kolik by to bylo lepší. Jestli to vůbec stojí za to.
Pokud budu chtít opravdu, ale opravdu vysokou rychlost čtení, nezbývá mi nic jiného, než celou databázi držet v paměti, nebo to provozovat na SSD disku.
Celou v paměti můžeš držet i relační databázi a bude to pekelně rychlé.
Protože nejsem jeden z těch majetných, zvolil jsem si jako optimální první možnost
To jako že GB v RAM vyjde levněji než GB na SSD?
a druhou třeba přidat časem, pokud o to bude zájem ze strany uživatelů, if any.
Co znamená přidat podporu SSD? Na jaké úrovni abstrakce se chceš pohybovat? Budeš pracovat se souborovým systémem nebo přímo s blokovým zařízením? Jak vůbec tvoje aplikace pozná, že je to SSD a ne HDD? Bude tam nějaký principiální rozdíl nebo to pro aplikaci bude prostě jen rychlý disk?
Zápis bude ta nejpomalejší věc na tom všem a veškerá úspora rychlosti při čtení se klidně může na zápisu odrazit. Takže asi nebudu vyloženě hrotit nějakou složitost zapisování, nebo počet hashování.
Zvaž ten asynchronní zápis, co jsem popisoval výše – příchozí data uložit nejdřív do logu (bezpečné – už se neztratí, můžeš potvrdit jejich přijetí) a až asynchronně je hashovat a dále zpracovávat a „publikovat“ pro čtení. Parametrem zápisu by mohlo být zda má být synchronní (čtecí operace uvidí nová data hned po potvrzení zápisu, ale bude to pomalejší) nebo asynchronní (rychlejší potvrzení, viditelnost později).
Ano, nepůjdou vytvářet relace, ale od toho tu nejsme, tím bych současnou argumentaci tohohle bodu uzavřel.
Co myslíš slovem relace?
V argumentech zadávat přímo dotaz a výstupem budou čistá data pro snadné parsování v programech.
Takový nástroj jsem si napsal pro SQL/LDAP: SQL-DK.
ale cílem není použít něco existujícího, ale vytvořit něco vlastního.
Takže to má být jen cvičení?
[1] možná tak u webů, kde je často zvykem nasadit nějaký super-moderní-cool web, pak nějakou dobu trpět, pak to celé zahodit a začít znova – ale normální software se takhle nedělá
Takže to má být jen cvičení?
Což je asi ten nejlepší start. Zkusí si to napsat, pochopí, že některé věci jsou mnohem složitější, než se zdá (paralelní čtení, no to ještě jde, ale co takhle paralelní zápisy), zjistí, že hash není všechno a že je daleko podstatnější způsob, jakým je to uloženo na disku (a nakonec i v paměti). Apod. A potom se dostane k již desítky let hotovým projektům, které to mají vše dávno vyřešené (a hlavně bude vědět jak a proč). Takže za mě palec nahoru.
Což je asi ten nejlepší start.
To je, ale mohl to napsat dřív než v P.S.
Treba... nebo respektive (od 29 minuty) Jinak je to podrobne rozebrane snad v kazde Datove knizce.SQLNějaké konkrétní výhrady?
Datove knizce
Chvíli jsem přemýšlel, jestli to mělo být podle vzoru Datová schránka, nebo podle spoluautora Třetího manifesta. @_@
Jenže ty problémy jsou často už vyřešené (či alespoň řešitelné v rámci SQL) nebo ta Datova kritika spočívá v tom, že SQL nedostatečně využívá potenciálu relačního modelu. Spíš mě zajímala odpověď od toho, kdo chce zavrhnout relační model jako takový. Tyhle výhrady se nedají vyřešit tím, že místo něj začneš používat nějakou hashovací tabulku.
Datova kritika spočívá v tom, že SQL nedostatečně využívá potenciálu relačního modeluSQL != relacni databaze. Datova a Darwenova kritika SQL spociva v tom, ze (1) neobsahuje veci z rel. modelu, jak rikas, (2) ma veci mimo rel. model.
Jenže ty problémy jsou často už vyřešenéNejsou a ani nebudou, protoze jsou nedilnou soucasti jazyka, z tech nejzjevnejsich, napr. NULL, SELECT majici ruzne vyznamy, duplicitni radky, ...
Někde tam vytýkají věci, které se týkají jen některých implementací – např. že 'A' = 'A '
ale length('A') < length('A ')
V MySQL:
# sql-dk --db mysql --sql "SELECT :a1 = :a2, length(:a1) = length(:a2)" --data-named a1 'A' a2 'A ' ╭─────────────────────┬─────────────────────────────────────╮ │ 'A' = 'A ' (BIGINT) │ length('A') = length('A ') (BIGINT) │ ├─────────────────────┼─────────────────────────────────────┤ │ 1 │ 0 │ ╰─────────────────────┴─────────────────────────────────────╯ Record count: 1
Ale v PostgreSQL:
# sql-dk --db postgres --sql "SELECT :a1 = :a2, length(:a1) = length(:a2)" --data-named a1 'A' a2 'A ' ╭─────────────────┬─────────────────╮ │ ?column? (bool) │ ?column? (bool) │ ├─────────────────┼─────────────────┤ │ false │ false │ ╰─────────────────┴─────────────────╯ Record count: 1
A že když si někdo předefinuje operátor pro rovnost, aby vracel něco jiného než rovnost, a pak to dělá blbosti – tak to bych opět nepovažoval za vadu jazyka, ale za chybu toho, kdo si ten operátor předefinoval.
Ten NULL může být trochu záludný, ale svůj smysl to má – když ho chápeš jako nedefinovanou/neznámou hodnotu, tak nemůžeš říct, že dvě nedefinované hodnoty jsou si rovné – a nemůžeš ani říct, že jsou různé → výsledkem je opět NULL (neznámá hodnota).
# sql-dk --db postgres --sql "SELECT null::text = 123::text, null::text = null::text, null, 123::text" ╭─────────────────┬─────────────────┬────────────────────┬─────────────╮ │ ?column? (bool) │ ?column? (bool) │ ?column? (unknown) │ text (text) │ ├─────────────────┼─────────────────┼────────────────────┼─────────────┤ │ null │ null │ null │ 123 │ ╰─────────────────┴─────────────────┴────────────────────┴─────────────╯ Record count: 1
# sql-dk --db mysql --sql "SELECT null = 123, null = null, null, 123" ╭─────────────────────┬──────────────────────┬─────────────┬──────────────╮ │ null = 123 (BIGINT) │ null = null (BIGINT) │ NULL (NULL) │ 123 (BIGINT) │ ├─────────────────────┼──────────────────────┼─────────────┼──────────────┤ │ null │ null │ null │ 123 │ ╰─────────────────────┴──────────────────────┴─────────────┴──────────────╯ Record count: 1
Maximálně se z té neznámé hodnoty někdy po přetypování na boolean
stane false
:
# sql-dk --db postgres --sql "SELECT CASE WHEN null THEN 1 ELSE 2 END" ╭─────────────╮ │ case (int4) │ ├─────────────┤ │ 2 │ ╰─────────────╯ Record count: 1
Ale to je takový obecný problém: co udělat při převodu ze třístavové hodnoty (true/false/null) na dvoustavovou (true/false). Buď by to mohlo vyhodit výjimku nebo se null
převede na false
. Možná by ta výjimka byla vhodnější – a pak by bylo potřeba explicitně uvádět, jak chceme null
interpretovat – třeba jako true
:
# sql-dk --db postgres --sql "SELECT CASE WHEN coalesce(null, true) THEN 1 ELSE 2 END" ╭─────────────╮ │ case (int4) │ ├─────────────┤ │ 1 │ ╰─────────────╯ Record count: 1
Ten NULL může být trochu záludný, ale svůj smysl to má – když ho chápeš jako nedefinovanou/neznámou hodnotu, tak nemůžeš říct, že dvě nedefinované hodnoty jsou si rovné – a nemůžeš ani říct, že jsou různé → výsledkem je opět NULL (neznámá hodnota).
Což je, ehm, ten problém (sémanticky). How to Handle Missing Information without Using NULL
Tohle jsem teď zrovna četl
Ano, je to sémantický problém – nakonec se ale s tím nějak musíš poprat, bez ohledu na to, zda jsi v jedné tabulce našel atribut s NULL hodnotou, nebo zda jsi v jiné nenašel odpovídající záznam.
NULL má výhodu v tom, že můžeš relativně snadno měnit model a povinnost/nepovinnost určitých atributů (stačí přidat/odebrat NOT NULL u sloupečku). Zatímco bez NULL bys musel zásadně změnit strukturu – tabulka s atributem vs. tabulka bez atributu + atribut v samostatné tabulce – což by vyžadovalo větší změny v kódu aplikací. Nebo bys musel od začátku dávat prakticky každý atribut do samostatné tabulky – ale s tím by asi nikdo nechtěl pracovat…
A někdy je problém, když chceš popsat více stavů/atributů jednou atomickou hodnotou.
Např. máš čtyři stavy:
ale jen tři možné hodnoty (resp. druhy hodnot):
Nepročetl jsem to ještě úplně důkladně, ale zatím jsem nepochopil, proč jsou SALARY_UNK
a UNSALARIED
samostatné tabulky a ne jen booleovské atributy osob. Jen doufám, že tím důvodem nebylo to, aby mohli v závěru zkritizovat SQL za to, že neumožní vložit jednotlivě záznamy do tabulek (resp. je potřeba použít odložené kontroly integrity – až při dokončení transakce). Aby měla každá osoba uvedený plat nebo příznak SALARY_UNK
či UNSALARIED
(právě jednu z těchto tří možností), jde zajistit i omezením na úrovni tabulky – není potřeba to rozbít na několik tabulek (a dokonce je to i uživatelsky přívětivější).
což by vyžadovalo větší změny v kódu aplikací. Nebo bys musel od začátku dávat prakticky každý atribut do samostatné tabulky – ale s tím by asi nikdo nechtěl pracovat…
Úder hlavičky na hřebíček. Knocked out.
Nedávno jsem viděl infografiku, která "vysvětlovala" přirozené spojení a odvozené operace v SQL pomocí Vennových diagramů – úplně blbě. Dostalo to hodně přes tři tisíce upboatů v /r/programming.
Ale v PostgreSQL:Typ
text
neodpovida standardu SQL. Ten problem se tyka typu char
.
Ten NULL může být trochu záludný, ale svůj smysl to má – když ho chápeš jako nedefinovanou/neznámou hodnotu,Tady uz mas problem hned na zacatku. Jaky je vlastne smysl NULL? Je to NULL nedefinovana, nebo neznama hodnota?
NULL je další možná hodnota dat. Tečka. Nic více, nic méně. Jejím smyslem je v první řadě být další možnou hodnotu v datech. Konec definice NULL.Joooo... kdyby byl svet tak jednoduchy... S touto definici by nesouhlasil ani E.F.Codd ani kdokoliv, kdo do databazi trochu vidi. Ve skutecnosti NULL neni hodnota, ale priznak absence hodnoty, tedy nemuze to byt hodnota. Fakt, ze NULL neni hodnota, lze overit treba pomoci NULL = NULL. Doporucuji si rozsirit obzory a precist treba: C.J.Date: SQL and Relational Theory: How to Write Accurate SQL Code
Vidím, že jste na velmi tenké půdě, když svou pravdu potřebujete podepřít „autoritou“ a knihou.Nikoliv, narozdil (treba od astrologu) jsem schopen dokladat skutecnosti i na zaklade relevantnich zdroju. Mimochodem, s tou autoritou v uvozovkach se jen ztrapnujete.
Nic na tom nemění fakt, že binární operace „=“ není ekvivalencí v matematickém smyslu, protože tato binární operace není reflexivní.Tak tohle je fakt konecna. Co bude dal? Komutativni implikace?
Jděte se zdroji do háje.Ano.
vše bylo samovysvětlující.Ne, nebylo. Rekl jste, ze NULL je hodnota a basta.
V rámci SQL existuje předem daná algebra, zahrnující i NULL hodnotu. Protože NULL je asymetrická, i některé párové funkce dávají jiné výsledky, pokud se připletou do toho NULL hodnoty, tak je algebra definována. Tedy „+“ a „SUM“, které bylo v jiném výsledku, atd.Tady hazete pojmama, ktere nedavaji smysl. V astrologicke poradne to mozna zabira, na me ne.
To je v tomhle kontextu celkem jedno – tak ani tak nemůžeš null
interpretovat jako true
nebo false
, může to být potenciálně obojí. Asi by bylo lepší, se toho držet a za žádných okolností takovou interpretaci/přetypování nepřipustit → mělo by to vyhodit výjimku, pokud nelze jinak – v opačném případě explicitně uvedeš, co null
znamená: coalesce(null, jeho_hodnota)
.
Vím, že těch významů může být víc (neznámá hodnota vs. nic atd.), musíš si prostě jeden vybrat a tak se k tomu NULL v aplikaci chovat.
Nebo by bylo lepší mít vedle NULL ještě další symboly jako ?, * atd.?
Pořád mi přijde pro uživatele stravitelnější, když mají v nějakém sloupečku sem tam NULL, než kdyby měli model rozpadlý na milion tabulek a museli je neustále propojovat. BTW: co by se v těch sloupečcích po propojení tabulek zobrazovalo v případě neexistence odpovídajícího záznamu v druhé tabulce, když bychom neměli NULL? Bylo by takové spojení nemožné a bylo by potřeba položit víc dotazů a data si poskládat až v aplikaci?
NULL je to samé, co ve floating point číslech NaNNeni. Viz vyse;
Díky tomu, že existuje NULL (v databázích) a NaN (ve fpu) má každá – i chybná operace – definovaný výsledek. Díky těmto speciálním hodnotám jsou všechny funkce a operace vždy dobře definované a vždy dávají výslednou hodnotu.Vazne?
SELECT 1 + NULL; -> NULL CREATE TABLE foo (a int); INSERT foo VALUES (1); INSERT foo VALUES (NULL); SELECT sum(a) FROM foo; -> 1
Díky tomu, co jsem popsal v předchozím odstavci, se to celé matematicky velmi výrazně zjednodušilo. Stejně tak jako všechny operace.Nerekl bych, protoze 3VL rozbiji rel. model, na kterem jsou rel. db postaveny. Treba neplati (R JOIN R) = R. Lze overit treba na vyse uvedene tabulce:
SELECT * FROM foo f1 NATURAL JOIN foo f2;
Nějak jsem nepochopil, v čem se mnou nesouhlasíte.Nesouhlasil jsem s: operace vždy dobře definované a vždy dávají výslednou hodnotu.
máte neflexibilní mozek, který Vám říká, že operace „+“ a skupinová funkce „SUM“Omlouvam se, priste uz zadna cisla scitat nebudu.
Omlouvam se, priste uz zadna cisla scitat nebudu.
Je přece rozdíl, když a) sčítám celou tabulku, kde jsem si definoval, že sloupec není NOT NULL a tudíž v něm můžou být chybějící hodnoty, a tím, když b) explicitně říkám, že chci sečíst dvě čísla/proměnné a jedno z nich je NULL.
V případě a) čekám, že se chybějící hodnoty přeskočí, zatímco v případě b) je fakt divné, že bych chtěl sčítat třeba 1 a NULL a je dobře, že výsledkem je NULL a všimnu si toho – dostanu nedefinovanou hodnotu.
Pokud ti tohle chování přijde divné nebo ho nechápeš, nikdo ti přece nebrání si všechny sloupce nadefinovat jako NOT NULL a/nebo používat coalesce(potenciálně_null_proměnná, hodnota_pro_případ_null)
.
Pokud ti tohle chování přijde divné nebo ho nechápeš,To neni podstatne. Jde o to, ze ani v tak banalni situaci jako je scitani cisel se SQL nechova konzistentne.
Jsou to jiné funkce/operace, tak je i výsledek jiný:
$ sql-dk --db blog --sql "SELECT sum(hodnoty) FROM (SELECT unnest(array[1,2,null,3]) AS hodnoty) AS x" ╭────────────╮ │ sum (int8) │ ├────────────┤ │ 6 │ ╰────────────╯ Record count: 1 $ sql-dk --db blog --sql "SELECT 1 + 2 + NULL + 3" ╭─────────────────╮ │ ?column? (int4) │ ├─────────────────┤ │ null │ ╰─────────────────╯ Record count: 1
Jestli chceš sečíst jen neNULLové hodnoty a NULL přeskakovat, tak to dělej tím prvním způsobem. Jestli chceš jen sečíst čísla, tak to dělej tím druhým. Tam by naopak bylo velmi zákeřné, kdyby se NULL chovalo stejně jako 0 a v tichosti se to na ni převedlo.
V obou případech je kdesi uvnitř obyčejné sčítání, ale funkce sum()
je i něco víc než jen pouhé a + b + c + …
.
Jsou to jiné funkce/operaceTo ja pochopitelne vim. Ukazal jsi ten muj priklad jen s vice hodnotami. Mne vadi, ze ty dve operace se nechovaji vzajemne konzistentne. Konzistentni by bylo, aby SUM pri vyskytu NULL vracelo taky NULL jako scitani.
Konzistentni by bylo, aby SUM pri vyskytu NULL vracelo taky NULL jako scitani.
Taková funkce by klidně mohla existovat, akorát by se asi moc nepoužívala. Stejně tak by mohl existovat operátor pro sčítání, který by NULL nahradil nulou a v tichosti přičetl. Opět by to asi nikdo nepoužíval. Ale můžeš si takové funkce/operátory vytvořit. Standardně tam máš prostě věci, které dávají smysl a které se budou používat.
Jsou to jiné funkce/operace, tak je i výsledek jiný:
sum(a, b) != a + b
? Sorry, ale to při nejlepší vůli není konzistence, to má deda.jabko naprostou pravdu...
Přesně tak, jsou to jiné funkce. Když si dáš tu práci a ručně vyjmenuješ a + b + c + d + …
, tak proč bys tam cpal NULL? Leda omylem! A pak je správně že i na výstupu vyleze NULL.
Něco jiného je, když sčítáš hodnoty z celé tabulky, kde můžou některé hodnoty chybět – to je legitimní stav a předpokládá se, že chceš sečíst jen ty, které tam jsou a prázdná místa přeskočit.
Co se týče konzistence – tu tady nemá cenu řešit – je to jako kdybys řešil (ne)konzistenci sčítání a násobení a divil se, že dávají jiné výsledky – ano, protože to jsou jiné funkce. Konzistenci smysl řešit v otázkách typu: chovají se všechny agregační funkce k NULL stejně? Nebo: chová se +,-,*,/ k NULL stejně?
Když se vrátím ještě k tomu a + b + c + d + …
, pokud víš, že tam budeš dávat i NULL hodnoty a chceš je přeskakovat, tak to tomu SQL řekni:
SELECT 1 + coalesce(NULL, 0)
nebo třeba:
SELECT sum(hodnoty) FROM (SELECT unnest(array[1,null]) AS hodnoty) AS x
Co je to za smajlíka? Magnetofon s tlačítkem?
BTW: co ti dá za výsledek 1 + ∞ ?
Něco jiného je, když sčítáš hodnoty z celé tabulky, kde můžou některé hodnoty chybět – to je legitimní stav a předpokládá se, že chceš sečíst jen ty, které tam jsou a prázdná místa přeskočit.
Agregační funkce. Funkce. Zobrazení.
Tak znovu: co ti dá za výsledek 1 + ∞ ? Podobné je to s tím NULL.
Kdyby byla funkce sum()
jen primitivní sčítání, tak by taky měla vracet NULL, pokud by se někde mezi výsledky objevilo. Ale taková funkce by byla na nic a téměř nikdo by ji nepoužíval – když agregujeme nebo třeba počítáme průměr, zajímají nás známé hodnoty a ty chybějící přeskočíme. Dosadit za neznámé hodnoty nulu by byla chyba – např. ten průměr by nám vyšel úplně jinak, blbě.
Oproti tomu sčítání pomocí operátoru + propaguje NULL hodnotu do výsledku. Dejme tomu, že máme tabulku platů a u některých lidí máme NULL – nevíme, kolik berou. A teď někdo rozhodne, že všem přidá deset korun. Kdyby výsledkem 10 + NULL bylo totéž co 10 + 0, bral by teď ten člověk jen 10 Kč, místo přidání by si patrně hodně pohoršil. Zmizela by ta informace „nevíme kolik bere“ a místo ní bychom zapsali určitou, ale nesmyslnou hodnotu. Pravda je ale taková, že stále nevíme, kolik ten člověk bere – proto je výsledkem takového součtu NULL – jen víme, že má mít teď o 10 víc, ale to je celkem irelevantní, protože nevíme, kolik měl předtím → takže prostě stále NULL, neznámá hodnota.
To SQL je navrženo i s ohledem na praktické použití – není to jen teoretické cvičení, byť by se to některým líbilo. Někdo by třeba chtěl funkce pro medián a průměr vracející nesmysly nebo třeba zákeřný operátor + požírající informace o tom, že hodnota chybí, a dosazující v tichosti nuly… ale naštěstí to funguje jinak.
Kdyby byla funkce sum() jen primitivní sčítání, tak by taky měla vracet NULL, pokud by se někde mezi výsledky objevilo. Ale taková funkce by byla na nic a téměř nikdo by ji nepoužíval – když agregujeme nebo třeba počítáme průměr, zajímají nás známé hodnoty a ty chybějící přeskočíme.Proc by ji nepouzival? Ono v SQL nejde udelat:
SELECT sum(x) FROM foo WHERE x IS NOT NULL?Jasne to rika, co se ma udelat a nemusi se predpokladat, ze sum se chova jinak nez bezne scitani.
Oproti tomu sčítání pomocí operátoru + propaguje NULL hodnotu do výsledku. Dejme tomu, že máme tabulku platů a u některých lidí máme NULL – nevíme, kolik berou. A teď někdo rozhodne, že všem přidá deset korun. Kdyby výsledkem 10 + NULL bylo totéž co 10 + 0, bral by teď ten člověk jen 10 Kč, místo přidání by si patrně hodně pohoršil.
Někdo by třeba chtěl funkce pro medián a průměr vracející nesmysly nebo třeba zákeřný operátor + požírající informace o tom, že hodnota chybí, a dosazující v tichosti nuly…Tohle je straw man, tohle tady nikdo nechce.
SELECT sum(x) FROM foo WHERE x IS NOT NULL?Jasne to rika, co se ma udelat a nemusi se predpokladat, ze sum se chova jinak nez bezne scitani.
Vedle toho sum(x)
můžeš chtít počítat i jiné agregace (jiné funkce nebo nad jinými sloupci, které jsou děravé v jiných místech). Se současnými funkcemi to zvládneš na jeden průchod tabulkou. V tom „čistějším“ řešení bys musel položit víc dotazů, bylo by to nepraktické.
Se současnými funkcemi to zvládneš na jeden průchod tabulkou.Nedava smysl zaobirat se timto problemem, jelikoz SQL nikde nerika, jak ma byt dotaz zpracovan.
V tom „čistějším“ řešení bys musel položit víc dotazů, bylo by to nepraktické.Tak bys mel vic poddotazu, ktere pocitaji presne to, co chces, misto toho, co si nekde myslel, ze asi budes chtit. Optimalizator si to uz prebere.
Takže bych napsal třeba:
SELECT (SELECT sum(sloupec1) FROM tabulka WHERE sloupec1 IS NOT NULL), (SELECT sum(sloupec2) FROM tabulka WHERE sloupec2 IS NOT NULL);
místo:
SELECT sum(sloupec1), sum(sloupec2) FROM tabulka;
Myslíš, že o takové „zlepšení jazyka“ někdo stojí?
IMHO by velice rychle někdo napsal funkci sum_skip_nulls()
a ten původní sum()
by nikdo nepoužíval. Což asi autory napadlo hned na začátku, takže napsali funkci sum_skip_nulls()
a pojmenovali ji sum()
.
ja teda tu diskuzi moc pozorne necelt, ale nespocivala kritika dedy.jabka mimo jine v tom, ze NULL je jakysi specialni objekt/hotnota/whatever, prave neco jako ∞?BTW: co ti dá za výsledek 1 + ∞ ?
Ano, je speciální, ale matematici taky pracují s ∞.
Přesně tak, jsou to jiné funkce.Co jsem se koukal naposled, tak suma a sčítání je jedno a totéž. Rozumím tomu, že v SQL tomu tak není, ale to neznamená, že to nekonzistetní není a že to není bordel. Je to bordel
Rozhodně nelze předpokládat, že NULL = 0. To by nevadilo možná tak u toho sčítání, ale jakmile začneš počítat průměry, mediány atd., máš problém.
Možná by se někdy hodilo, kdyby ty agregační funkce vracely složenou hodnotu, která by obsahovala:
ale to spíš tak pro zajímavost.
Ono to konzistentní je, v rámci skupin: agregační funkce vs. matematické operátory. Že zrovna sum()
a +
mají k sobě blízko a někteří lidé si to pletou… to je okrajová záležitost – je to jedna z mnoha funkcí a jeden z mnoha operátorů. Spíš náhodná podoba.
Jak bys tu údajnou nekonzistenci chtěl vyřešit?
Proč je špatně 1) je vysvětlené tady: #88 a proč 2) tady: #92. Dává dobrý smysl, proč je to tak, jak to je.
S tou 3) je to trochu složitější, ale v zásadě jde o to, že neexistence NULL by přinesla více problémů, než by vyřešila. Jednak by to bylo uživatelsky dost nepřívětivé, viz #29. A jednak by to byl v podstatě odklon od relací a příklon spíš k nějakým objektovým databázím, viz #62 – výsledkem dotazu by nebyla relace ale jakási stromová struktura…1 A znamenalo by to vlastně mít typované2 NULL hodnoty (kterým by se ovšem nesmělo říkat NULL) aneb prázdné zanořené relace, viz #62.
Nakonec se s tou prázdnotou (chybějící hodnotou) musíš nějak vyrovnat – bez ohledu na to, zda byla vyjádřena ve formě NULL hodnoty, nebo prázdné relace – např. když budeš počítat absolutní hodnotu a na vstupu dostaneš prázdnou relaci (místo NULL), co bude na výstupu? Opět prázdná relace? Nebo nula? Nebo vyhodíš výjimku? Jak je vidět, použití prázdných relací místo NULL nic neřeší. Naopak jen přidává další složitost – taková prázdnota může být navíc vnitřně strukturovaná, zatímco u těch NULL hodnot jsme omezeni více méně na primitivní datové typy.
[1] což přiznávám, je mi svým způsobem i trochu sympatické, protože by to umožnilo pracovat s vnořenými strukturami jednotným způsobem, zatímco teď musím používat různé funkce, podle toho, zda pracuji s polem, s XML, s JSONem atd.
[2] ve skutečnosti jsou typované už teď, ale těch typů je méně resp. nemívají tak složitou strukturu – např. SELECT null::int4
vrací sloupec typu int4
a NULL hodnotou
Rozhodně nelze předpokládat, že NULL = 0.V kontextu SQL určitě ne - už z toho je IMHO vidět nešťastné pojmenování NULL.
Jak bys tu údajnou nekonzistenci chtěl vyřešit?Tak, jak jsem naznačil těmi odkazy, tzn. nepoužít jeden NULL na všechny případy, kdy hodnota je neplatná, nemáme ji, není použitelná atd., ale použít zvlášť pro každý z těch případů typ s dobře definovaným chováním. Pro začátek třeba trojice NotPresent, NotApplicable a Invalid, s tím, že by se nepřetypovávaly automaticky na čísla.
Rozhodně nelze předpokládat, že NULL = 0.V kontextu SQL určitě ne - už z toho je IMHO vidět nešťastné pojmenování NULL.
NULL != ZERO a vlastně nikde mi nepřijde dobré zaměňovat NULL a 0, jsou to úplně jiné věci. Např. když budu mít na skladě 0 položek, tak vím, že tam žádné nejsou; ale když tam budu mít NULL položek, tak vím, že nic nevím
nepoužít jeden NULL na všechny případy, kdy hodnota je neplatná, nemáme ji, není použitelná atd., ale použít zvlášť pro každý z těch případů typ s dobře definovaným chováním. Pro začátek třeba trojice NotPresent, NotApplicable a Invalid, s tím, že by se nepřetypovávaly automaticky na čísla.
Takže vlastně problém není v tom, že SQL má speciální typ NULL navíc, ale že má těch speciálních typů málo?
Na jednu stranu to zní dobře, umožňovalo by to popsat víc speciálních stavů a už se to celkem blíží výjimkám, ale na druhou stranu mám obavu, že by to systém příliš zesložitilo.
Ty speciální hodnoty by byly globální nebo by si je mohl definovat uživatel?
Ať tak či tak, co by bylo výsledkem operací:
SELECT 1 + 2 + NotPresent + 3 + NotApplicable + Invalid + 4;
nebo:
SELECT sum(unnest) FROM unnest(array [1, 2, NotPresent, 3, NotApplicable, Invalid, 4]);
? Jak bys sloučil více speciálních hodnot na vstupu do jedné hodnoty na výstupu?
Měly by tyhle speciální hodnoty nějaký společný nadtyp nebo bych je musel ve všech funkcích ošetřovat jednotlivě?
Pokud by byly uživatelsky definované, mohl bych vytvořit např. typ/doménu: „všechna kladná čísla + 0 + NotPresent + Invalid“, což by se mohlo někdy hodit. Ale jak bych s tím pak pracoval ve standardních funkcích – např. bych chtěl sečíst hodnoty nebo spočítat druhou mocninu? Jak by ta funkce věděla, co má dělat v případě NotPresent
a co v případě Invalid
?
Ten společný nadtyp by byl asi nutnost – a mohli bychom mu říkat NULL
P.S. ještě k tomu Option v Rustu – je to zajímavé, ale řeší to trochu jiný problém – není to zrušení NULL (jen se mu říká None), ale užitečné je to pro signalizaci, zda hodnota může nebo nemůže být NULL. A k tomu se v SQL používá NOT NULL
u sloupců nebo třeba anotace @NotNull
v některých programovacích jazycích/frameworcích.
Nebyla náhodou smyslem zavedení relačního modelu mj. eliminace ukazatelů? Nesmrdí NULL ukazateli?No, ani ne. NULL je hodnota, ktera neni znama/vyplnena/definovana. Mozna ze pro aplikacniho programatora nedava smysl (zvlast pokud jsi zvykly na C), ale treba v Haskellu nebo Ocamlu se bezne pracuje s tim, ze nektere hodnoty jsou volitelne, a ten jazyk na to ma konstrukty. SQL na to taky ma konstrukty.
Není náhodou agregační funkce v tom modelu zobrazení? Jak se hledá obraz, když vzor neexistuje?Moc nerozumim, co se snazis rict. Obraz se hleda uplne stejne pro NULL jako pro jakoukoli jinou hodnotu. Muzes to brat tak, ze agregacni funkce nahrazuji NULL za vhodny neutralni prvek podle sveho typu.
Nemají náhodou podle toho modelu být hodnoty z dané domény, ve které ale NULL jaksi není?NULL v te domene byt muze nebo nemusi. "bla number not null" ho v domene nema. "bla number" ho v domene ma. Je to tvoje volba. Pokud reknes ze dany slopec povoluje hodnotu typu NULL, ma pak domenu jeho typ + NULL, podobne jako muzes rozsirit realna cisla o nekonecna, pokud se ti to hodi. Neni prekvapive, ze se to casto hodi. Osobne se mi to, jak to delaji silne typovane funkcionalni jazyky libi vic, pokud je nejaka hodnota volitelna, musis ji explicitne vybalit a definovat co se stane. SQL je trochu dynamictejsi, takze dela nejaka pretypovani za chodu, coz je trochu zmatenejsi, ale zas praktictejsi na takove to bezne pouzivani.
Takže vlastně problém není v tom, že SQL má speciální typ NULL navíc, ale že má těch speciálních typů málo?No tak svým způsobem jo, resp. ten problém je, že NULL používá pro více specielních věcí, kde pokaždé má potenciálně jiné chování...
Ty speciální hodnoty by byly globální nebo by si je mohl definovat uživatel? Měly by tyhle speciální hodnoty nějaký společný nadtyp nebo bych je musel ve všech funkcích ošetřovat jednotlivě?Nevím. Nemůžeš od jednoho mého komentáře chtít specifikaci nového SQL standardu
SELECT 1 + 2 + NotPresent + 3 + NotApplicable + Invalid + 4;Protože Invalid signalizuje chybu, propagoval bych ho, takže Invalid. Pokud by tam nebyl, tak 10, řekl bych, ale je to jen návrh naprosto odboku. To samé pro
sum()
.
Mimochodem, jakej je use case takovýhohle selectu?
A k tomu se v SQL používá NOT NULL
Mmmkay, a pak uděláš outer join a NULL se ti tam dostane stejně, pokud se nemýlím...
Mmmkay, a pak uděláš outer join a NULL se ti tam dostane stejně, pokud se nemýlím...Nejenom v tomto pripade. Hezke je i tohle:
select a from foo where false; -> tabulka s 0 radky
select (select a from foo where false); -> tabulka s jednim radkem majici jeden atribut majici "hodnotu" NULL
Jako, musim rict, ze rozsirit relacni model o algebraicke datove typy by se mi hodne libilo. Jak pises niz, pro Option ze SML (Maybe z Haskellu, atd, pokud ho obsahuje i Rust, cobre pro nej) uz v SQL je nejaka podpora v ramci NOT NULL. Samozrejme by sis nad tim musel sam definovat logiku. Potgres umi jakesi vyctove typy (reknes, ze soupecek pohlavi muze byt "M", "F", "Petr Tomes", a nic jineho ti tam nedovoli vlozit) takze se to da i trochu udelat, ale prave ADT by byly trosku jinde. Souhlasim, ze to z predrecnika dela spis milovnika NULLu co by ho chtel rozsirit, nez jeho odpurce, i kdyz si to mozna nemysli. NULL v SQL je navic mnohem lepsi jak NULL treba v Jave, kde muze uplne cokoli byt uplne kdykoli null. V SQL muzu rict, ze nejaka hodnota musi byt vyplnena / definovana a ta databaze to zajisti, takze v tomhle je bliz Haskellu.Na jednu stranu to zní dobře, umožňovalo by to popsat víc speciálních stavů a už se to celkem blíží výjimkám, ale na druhou stranu mám obavu, že by to systém příliš zesložitilo.
Ty speciální hodnoty by byly globální nebo by si je mohl definovat uživatel?
Jako, musim rict, ze rozsirit relacni model o algebraicke datove typy by se mi hodne libilo. Jak pises niz, pro Option ze SML (Maybe z Haskellu, atd, pokud ho obsahuje i Rust, cobre pro nej) uz v SQL je nejaka podpora v ramci NOT NULL. Samozrejme by sis nad tim musel sam definovat logiku. Postgres umi jakesi vyctove typy (reknes, ze soupecek pohlavi muze byt "M", "F", "Petr Tomes", a nic jineho ti tam nedovoli vlozit) takze se to da i trochu udelat, ale prave ADT by byly trosku jinde. Souhlasim, ze to z predrecnika dela spis milovnika NULLu co by ho chtel rozsirit, nez jeho odpurce, i kdyz si to mozna nemysli. NULL v SQL je navic mnohem lepsi jak NULL treba v Jave, kde muze uplne cokoli byt uplne kdykoli null. V SQL muzu rict, ze nejaka hodnota musi byt vyplnena / definovana a ta databaze to zajisti, takze v tomhle je bliz Haskellu.
Že zrovna sum() a + mají k sobě blízko a někteří lidé si to pletou… to je okrajová záležitost – je to jedna z mnoha funkcí a jeden z mnoha operátorů. Spíš náhodná podoba.Takova argumentace stoji hodne na hlinenych nohach.
A jednak by to byl v podstatě odklon od relací a příklon spíš k nějakým objektovým databázím, viz #62 – výsledkem dotazu by nebyla relace ale jakási stromová struktura…Nesmysl, vysledkem je opet relace, ktera bude mit jako hodnoty opet relace, plne v souladu s rel. modelem, na kterem DB stoji. Priklad, ktery jsem uvadel, by v podstate mohl jit resit i prostredky SQL, protoze je pro to potreba jen projekce, restrikce a extenze, prip. sjednoceni, coz SQL umi. Ale neumi relace jako hodnoty. Z tohoto pohledu neni SQL dostatecne ortogonalni jazyk.
Nakonec se s tou prázdnotou (chybějící hodnotou) musíš nějak vyrovnat – bez ohledu na to, zda byla vyjádřena ve formě NULL hodnoty, nebo prázdné relaceNULL znamena absenci hodnoty. V pripade prazdne relace mas hodnotu, kterou je prazdna relace.
když budeš počítat absolutní hodnotu a na vstupu dostaneš prázdnou relaciCoze?
NULL znamena absenci hodnoty. V pripade prazdne relace mas hodnotu, kterou je prazdna relace.To si moc neumim predstavit. Rekneme, ze mas tahle data: titanic3.csv, jenom maji 150 000 000 radku, takze abys je mohl analyzovat, musis je nacpat do databaze. Je zjevne, ze tam hromada dat chybi. Lide kteri neprezili treba casto nemaji clun na kterem se zachranili, protoze se nezachranili, u nekoho se nevi, kde mel kajutu, takove veci. Jak vypada "prazdna relace" jako hodnota sloupce pro takove pripady? Jakym zpusobem by vypadalo dotazovani nad takovymi relacemi?
A ty bys fakt chtěl takhle dekomponovaný1 model používat?
[1] v podstatě: co původní sloupec, to tabulka
Lide kteri neprezili treba casto nemaji clun na kterem se zachranili, protoze se nezachranili, u nekoho se nevi, kde mel kajutu, takove veci. Jak vypada "prazdna relace" jako hodnota sloupce pro takove pripady?Cpat vsude prazdnou relaci misto NULL absolutne nedava smysl. Dejme tomu, ze kajuty maji cisla od 1 do 1000. Zavedes si sloupec "kajuta integer CHECK ((kajuta >= 1) AND (kajuta <= 1000))" a kdyz kajutu neznam, dam tam NULL. Vypada to rozumne, ze? Presto muzu udelat
SELECT avg(kajuta) FROM titanic WHERE survived = 1
a dozvedet se, jake bylo prumerne cislo kajuty, kde lidi prezili. Takova pitomost neni bug ale feature SQL.
Koncepcne mnohem logictejsi by bylo zavest vlastni datovy typ ,,cislo_kajuty'' jehoz domena bude obsahovat hodnoty { 1, ..., 1000, neznama-kajuta, nemel-kajutu }
, pro cez si definujes vlastni operatory, ktere davaji smysl.
Jakym zpusobem by vypadalo dotazovani nad takovymi relacemi?Jako jakekoliv jine dotazovani.
Cpat vsude prazdnou relaci misto NULL absolutne nedava smysl.S tim pojmem prisel kolega, takze moc netusim co to melo byt
Dejme tomu, ze kajuty maji cisla od 1 do 1000. Zavedes si sloupec "kajuta integer CHECK ((kajuta >= 1) AND (kajuta <= 1000))" a kdyz kajutu neznam, dam tam NULL. Vypada to rozumne, ze? Presto muzu udelatJasne. Ale to si v zasade jenom stezujes, ze SQL je dynamicky typovane a pouziva normalni datove typy. To, ze cislo kabiny je cislo, neznamena, ze ma smysl pocitat z nej prumer, ale podobne jako v Pythonu nebo Lispu to neznamena, ze Ti to ten typovy system zakaze. Jak jsem rikal, DB s ADT by byla zajimava, ale aktualne nemam pocit, ze se tim nekdo zabyva.SELECT avg(kajuta) FROM titanic WHERE survived = 1
a dozvedet se, jake bylo prumerne cislo kajuty, kde lidi prezili. Takova pitomost neni bug ale feature SQL. Koncepcne mnohem logictejsi by bylo zavest vlastni datovy typ ,,cislo_kajuty'' jehoz domena bude obsahovat hodnoty{ 1, ..., 1000, neznama-kajuta, nemel-kajutu }
, pro cez si definujes vlastni operatory, ktere davaji smysl.
Když můžeš s relací pracovat jako s kterýmkoli jiným datovým typem, tak ji můžeš předat jako parametr funkce a ty funkce musí nějak řešit výjimečný stav (prázdná relace nebo NULL) a buď vracet totéž (prázdná relace nebo NULL) nebo vyhazovat výjimku, případně si něco domyslet, ale to není ideální. Ano, když ta relace bude prázdná, tak se na ní ty funkce vůbec nepustí (není nad čím), ale to se týká případu, kdy funkci pouštíš nad jednotlivými záznamy/atributy, ale ne nad celou relací. (dá se to použít v případě, kdy tu novou vypočtenou hodnotu chci mít v té vnořené relaci) Navíc ta relace má další nevýhodu (resp. vyšší složitost) v tom, že tam můžeš být a) žádná hodnota, b) jedna hodnota, c) několik hodnot - zatímco u toho sloupce s možností NULL máš jen a) a b). Představ si např. že např. připojíš data z jiné tabulky tím tvým způsobem (ne klasický JOIN) a napojíš si tím k záznamu dvě další souvisejí hodnoty z jiné tabulky - tam ale nic nebude a místo NULL tam budeš mít prázdnou relaci. A ty pak z těchto dvou sloupců budeš chtít něco spočítat (absolutní hodnotu, maximum atd.) a mít to ve třetím sloupci.když budeš počítat absolutní hodnotu a na vstupu dostaneš prázdnou relaciCoze?
Když můžeš s relací pracovat jako s kterýmkoli jiným datovým typem, tak ji můžeš předat jako parametr funkce a ty funkce musí nějak řešit výjimečný stavVtip je v tom, ze prazdna relace neni vyjimecny stav, ale bezna hodnota.
Navíc ta relace má další nevýhodu (resp. vyšší složitost) v tom, že tam můžeš být a) žádná hodnota, b) jedna hodnota, c) několik hodnot - zatímco u toho sloupce s možností NULL máš jen a) a b).Vtip je v tom, ze je tam vzdy prave jedna hodnota a tou je ta relace. Kolik obsahuje n-tic, je podruzne.
A ty pak z těchto dvou sloupců budeš chtít něco spočítat (absolutní hodnotu, maximum atd.) a mít to ve třetím sloupci.Nejak nevidim v cem je problem. Stejne jako mas funkce definovane pro normalni tabulky (tj. relace), tak je pouzijes na vnorene relace.
Představ si např. že např. připojíš data z jiné tabulky tím tvým způsobem (ne klasický JOIN) a napojíš si tím k záznamu dvě další souvisejí hodnoty z jiné tabulky - tam ale nic nebude a místo NULL tam budeš mít prázdnou relaci. A ty pak z těchto dvou sloupců budeš chtít něco spočítat (absolutní hodnotu, maximum atd.) a mít to ve třetím sloupci.Pokud chces s tema hodnotama neco pocitat, nedava smysl, pouzit tuto "nahradu vnejsiho spojeni", ale dava smysl pouzit normalni spojeni.
absolutní hodnotuCo myslis tou absolutni hodnotou?
Pokud chces s tema hodnotama neco pocitat, nedava smysl, pouzit tuto "nahradu vnejsiho spojeni", ale dava smysl pouzit normalni spojeni.Ale to musí buď existovat NULL a použiješ RIGHT/LEFT JOIN nebo tam ten řádek bude úplně chybět - kvůli jedné chybějící hodnotě, bys přišel o další, které v pohodě spočítat šlo - takže bys musel pustit dva dotazy - místo toho, abys v některých sloupcích měl NULL a věděl, že tahle jedna hodnota chybí resp. nešlo ji vypočítat.
Co myslis tou absolutni hodnotou?Matematicky, absolutní hodnota čísla - na tom celkem nesejde, prostě nějaká funkce.
Imho docela hezkým příkladem, jak se vyhnout použití null, je Option v jazyce RustOption je uz i v Jave. ;-] Jinak tahle feature ma prapuvod ve funkcionalnich jazycich, nevim, jestli to driv mel SML nebo Haskell, ale tam proste dava smysl, aby funkce vzdy vracela nejakou hodnotu.
Praxe, na rozdíl od akademických teorií typu CoddJeste stesti, ze se ta Coddova akademicka teorie v praxi vubec neujala... co by s ni vsichni ti praktici delali...
jenom že jste si ho vzal na pomocTak za prve. Odkazoval jsem na Datovu knizku a ne na Codda a to z toho duvodu, abyste si rozsiril vzdelani o databazich, ve kterem mate mezery. Za druhe. Kdo dokaze lip vysvetlit vyznam NULL v relacnim modelu Codd, nebo vy?
algebra nad databázemito je taky zajimave :-]]
"priznak", "specialni hodnota" a "hodnota v systemu definovanem tak, aby to hodnota byla" mi prijdou ekvivalentniNejsou. Hodnota je (hrube receno) abstraktni objekt, ktery nema zadnou fyzickou reprezentaci, proste to je jen hodnota, napr. 42. A potom mas reprezentaci teto hodnoty, napr. jako sekvenci bitu ulozenych nekde v pameti, napr. jako 0x2A nebo "42". I kdyz se jedna o ruzne reprezentace, porad predstavuji jednu konkretni hodnotu. V pripade rel. db. se na to muzes divat tak, ze system pracuje s relacemi jako hodnotami, ktere jsou na disku/v pameti reprezentovany jako tabulky. Z tohoto pohledu se pak muzes pri zpracovani dotazu divat na tabulky jako na promenne, jejichz hodnoty jsou relace a pracovat s nimi konzistentne pomoci rel. operatoru.
tak ani tak nemůžeš null interpretovat jako true nebo false
bylo lepší, se toho držet a za žádných okolností takovou interpretaci/přetypování nepřipustit
Nebo by bylo lepší mít vedle NULL ještě další symboly jako ?, * atd.?...a to jsou jedny z tech problemu SQL, o kterych je tu celou dobu rec.
Asi je chyba, že v tom CASE se převede null
na false
a čistější by bylo, kdyby výsledkem byla výjimka (a bylo potřeba explicitní coalesce(null, false)
). Ale znamená to snad, že bychom měli zavrhnout NULL jako takové? Já si myslím, že ne. Prázdná/chybějící hodnota je důležitá informace, kterou je potřeba nějak přenášet.
Takže bys radši víc symbolů? Jaké?
Nebo radši žádný, ani NULL? Pak by mne zajímala odpověď na moji otázku výše:
co by se v těch sloupečcích po propojení tabulek zobrazovalo v případě neexistence odpovídajícího záznamu v druhé tabulce, když bychom neměli NULL? Bylo by takové spojení nemožné a bylo by potřeba položit víc dotazů a data si poskládat až v aplikaci?
Ale znamená to snad, že bychom měli zavrhnout NULL jako takové? Já si myslím, že ne. Prázdná/chybějící hodnota je důležitá informace, kterou je potřeba nějak přenášet.To je problem nepruzneho typoveho systemu. Pokud je to opravdu hodnota, mela by byt soucasti domeny daneho typu.
co by se v těch sloupečcích po propojení tabulek zobrazovalo v případě neexistence odpovídajícího záznamu v druhé tabulce, když bychom neměli NULL? Bylo by takové spojení nemožné a bylo by potřeba položit víc dotazů a data si poskládat až v aplikaci?Pro toto existuje reseni, ktere ale vetsina databazistu nezkousne, protoze vyzaduje relace jako hodnoty, coz SQL nepodporuje. Uvazujme relaci "items":
id | item | price |
---|---|---|
1 | Item #1 | 100 |
1 | Item #2 | 200 |
1 | Item #3 | 150 |
2 | Item #X | 10 |
2 | Item #Y | 300 |
id | name |
---|---|
1 | Alice |
2 | Bob |
3 | Chuck |
1 | Alice |
|
||||||||
2 | Bob |
|
||||||||
3 | Chuck |
|
A co vícerozměrná pole?
$ sql-dk --db blog --formatter tabular-prefetching --sql "SELECT 'abc', array [[123, 321], [456, 654]] UNION SELECT 'def', array []::integer[]" ╭─────────────────┬───────────────────────╮ │ ?column? (text) │ array (_int4) │ ├─────────────────┼───────────────────────┤ │ abc │ {{123,321},{456,654}} │ │ def │ {} │ ╰─────────────────┴───────────────────────╯ Record count: 2
$ sql-dk --db blog --formatter xml --sql "SELECT 'abc', array [[123, 321], [456, 654]] UNION SELECT 'def', array []::integer[]"
<?xml version="1.0" encoding="UTF-8"?> <batchResult xmlns="https://sql-dk.globalcode.info/xmlns/batchResult"> <database name="blog"> <statement> <sql>SELECT 'abc', array [[123, 321], [456, 654]] UNION SELECT 'def', array [[],[]]::integer[]</sql> <resultSet> <columnHeader label="?column?" name="?column?" typeName="text" type="12"/> <columnHeader label="array" name="array" typeName="_int4" type="2003"/> <row> <column>abc</column> <column> <array> <item> <array> <item>123</item> <item>321</item> </array> </item> <item> <array> <item>456</item> <item>654</item> </array> </item> </array> </column> </row> <row> <column>def</column> <column> <array> </array> </column> </row> </resultSet> </statement> </database> </batchResult>
Případně v procedurálním SQL můžeš vracet sady záznamů… ale tohle prostě nabourává představu, že hodnota atributu by měla být nejlépe atomická → s každou atomickou hodnotou můžu pracovat stejným způsobem (a nepotřebuji speciální funkce pro extrakci složek z různých komplexních typů a jejich aktualizaci nebo mazání).
Ale myslím, že tohle je spíš politické rozhodnutí – ne něco, čemu by jazyk SQL principiálně bránil a k čemu by nešel přizpůsobit.
Případně bys místo vnořené relace mohl použít nějaký komplexní typ jako XML a v něm zanořovat hodnoty třeba do deseti úrovní.
Neříkám, že by se to někdy nehodilo
A co vícerozměrná pole?
Případně bys místo vnořené relace mohl použít nějaký komplexní typ jako XML a v něm zanořovat hodnoty třeba do deseti úrovní.Takhle by to asi slo, ale je to nekoncepcni lepeni dalsich vlastnosti, protoze pro pole/XML bys potreboval dalsi dotazovaci jazyk. Kdyz to budes mit jako relaci, muzes pouzit uz dotazovaci jazyk, ktery mas.
hodnota atributu by měla být nejlépe atomickáTohle pravidlo bere za sve, kdyz do databaze cpes JSON, XML, apod. coz dnes dela cim dal tim vic lidi.
db.games.aggregate([ { $match : { date : { $gt : ISODate("1999-08-01T00:00:00Z"), $lt : ISODate("2000-08-01T00:00:00Z") } } }, { $unwind : '$teams' }, { $match : { 'teams.won' : 1 } }, { $group : { _id : '$teams.name', wins : { $sum : 1 } } }, { $sort : { wins : -1 } }, { $limit : 5 } ]);SQL
SELECT abbrev, name, count(*) FROM winners JOIN team ON team.id = winners.winner WHERE date > '1999-08-01T00:00:00Z' AND date < '2000-08-01T00:00:00Z' GROUP BY winner, abbrev, name ORDER BY count(*) DESC LIMIT 5;
Jaky je vlastne smysl NULL? Je to NULL nedefinovana, nebo neznama hodnota?Pokud jde filosofičtější náhled tohoto konceptu, přijde mi lepší brát NULL ne jako neznámou či nedefinovanou hodnotu, ale spíš výraz nesmyslnosti samotného dotazu(/operace/reference/atd.), ve smyslu "Tento dotaz nemůže být zodpovězen" (viz Mu). To, že to reálné implementace nedodržují (viz mnohokát zmíněná suma sloupce, kde jsou některé hodnoty NULL) bych viděl jako upřednostnění pragmatické implementace nad idealistickou.
Jenže ty problémy jsou často už vyřešené (či alespoň řešitelné v rámci SQLten mismatch with host languages neni resitelny a predstavuje ten nejvetsi problem SQL (a jakychkoliv deklarativnich jazyku). To ale neznamena, ze je treba SQL zavrhovat, proste se to ujalo, je to ekonomicky uspesne (tedy minimalne pro Elisona a Wideniuse) a proste se to pouziva. Dokonce to doslo tak daleko, ze se na skolach neuci uz nic jineho.
ze se na skolach neuci uz nic jineho
Chrchly, chrchly. Já půl semestru bojoval s Relem. Java. Must. Die.
Doporučuji používat
ten mismatch with host languages neni resitelny a predstavuje ten nejvetsi problem SQL (a jakychkoliv deklarativnich jazyku).
Ono tohle je i otázka bezpečnosti a distribuovanosti systému – když databáze běží na jiném stroji a provozuje ji někdo jiný, tak mě asi nenechá, abych si v ní mohl spouštět libovolný kód ve svém oblíbeném jazyce (zrovna v tom, v kterém píšu aplikaci, protože těch jsou spousty). Bylo by potřeba nějaké RPC/RMI, zajistit přenos objektů1 z jednoho systému (aplikace) na druhý (databáze). Jazyk typu SQL je nejlepší řešení, pokud to nemá být vázané na jeden konkrétní jazyk (→ aplikace by šlo psát jen v jednom jazyce) nebo dokonce konkrétní proces (databáze přímo součástí aplikace → nelze k ní přistupovat z více aplikací).
Řešitelné je to nějakou překladovou vrstvou, ať už ORM nebo nějakým generátorem typu JOOQ. Je to vlastně podobné, jako když chceš volat vzdálené procedury z jiného systému – máš strojově čitelný popis rozhraní, vygeneruješ si klientské třídy/funkce pro svůj jazyk a pak už píšeš jen v něm.
I když nevidím ani tak velký problém v kombinaci více jazyků v rámci jednoho programu – taky to má svoje uplatnění a dá se s tím pracovat.
[1] složité stromy nebo spíš grafy obsahující cykly, reference na sebe sama…
single table, protože to chci co nejjednodušší
Njn. Můj první web byl v ANSI C (přes Apache a CGI), data měl v Trivial Database. Potom, bohužel, jsem byl ukecán že každý správný web musí být v php a samozřejmě MySQL. To mi naštěstí moc dlouho nevydrželo a teď je to PostgreSQL. "Klasické" relační DB se na určité pojetí webu příliš nehodí, leckdy je key-value nejlepším počátečním řešením. Jenže, potom přijdou dokumenty, json. Takže PostgreSQL. Umí být čímkoliv od "triviální" keyvalue db (hstore), přes "noSQL" dokumentovou databázi (bson) až po "klasickou" relační potvoru (pochopitelně historické pořadí bylo lehce jiné, postgres(ql) nebyl vždy SQL). S daty od pár set kB, až po stovky terabajtů.
Jenže, potom přijdou dokumenty, json. Takže PostgreSQLvy jste, pote co se objevil na scene json format cekal pet let a nic neprogramoval, nez to postgresql take implementoval?
Já bych spíše všem začátečníkům doporučil, a všude dokola doporučuji vyhnout se neseriózním programovacím jazykům.Začátečníci mají imho požadavky na "serióznost" jazyka úplně minimální, až nulové. Ty požadavky dokonce nemá ani mnoho profesionálů (konkrétně bod 2 se mi zdá dost sporný).
Zrovna ten bod 2 je dost důležitý, bez něj se můžeš dostat do dost nepříjemných situací jako:
máme software fungující na jen platformě verze X, ale knihovna, kterou potřebujeme vyžaduje verzi X+1a.k.a. The Python problem
Ostatně zkuste si to.Zkouším si to. Profesionálně (python) ;) Jinak tohle je třeba zrovna věc, která se dá docela úspěšně ovlivnit architekturou. Pokud je něco postaveno jako services/microservices, tak nevadí, že některé části běží třeba v cobolu (zrovna jsem na to v práci natrefil) a jiné části například ve starých verzích pythonu. Osobně se mi ta python sitace taky nelíbí, když na verzi 3.x všichni z vysoka kašlou, ale existují příklady, kde mi to vůbec nevadí (například D). Co naprosto nechápu je důvod, proč kompilátory/interpretry nemají vícero frontendů, kde ve zdrojáku by byla vyspecifikovaná verze se kterou to chci přeložit/interpretovat a tím by se celý problém vyřešil. Sice by to zvedlo velikost samotného překladače/interpretru, ale komu dneska není ukradená?
Co naprosto nechápu je důvod, proč kompilátory/interpretry nemají vícero frontendů, kde ve zdrojáku by byla vyspecifikovaná verze se kterou to chci přeložit/interpretovat a tím by se celý problém vyřešil. Sice by to zvedlo velikost samotného překladače/interpretru, ale komu dneska není ukradená?
#!/usr/bin/perl use v5.6.1;...
$ javac 2>&1 | grep target -target <release> Generate class files for specific VM version $ gcc --help | grep std -std=<standard> Assume that the input sources are for <standard>
single table, protože to chci co nejjednoduššíOn to vlastne neni problem, protoze nad jednou tabulkou se da vzdy udelat vice tabulek pomoci prefixu u klice. Jinak pokud si chces zablbnout a podivat se do zajimavych vod, muzes svou ulohu rozsirit na distribuovanou in-memory databazi.
zůstává ukryto
Ten přínos/smysl má pro tebe hodnotu NULL na škále od 1 do 10.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.