sql >> Databáze >  >> RDS >> Database

50 odstínů NULL – různé významy NULL v SQL

Tony Hoare, který je většinou označován jako vynálezce reference NULL, to nyní nazývá chybou za miliardu dolarů, kterou nyní „trpí“ téměř všechny jazyky, včetně SQL.

Cituji Tonyho (z jeho článku na Wikipedii):

Říkám tomu moje miliardová chyba. Byl to vynález nulové reference v roce 1965. V té době jsem navrhoval první komplexní typový systém pro odkazy v objektově orientovaném jazyce (ALGOL W). Mým cílem bylo zajistit, aby veškeré použití referencí bylo absolutně bezpečné, s kontrolou prováděnou automaticky kompilátorem. Ale nemohl jsem odolat pokušení vložit nulovou referenci, jednoduše proto, že to bylo tak snadné implementovat. To vedlo k nesčetným chybám, zranitelnostem a pádům systému, které za posledních čtyřicet let pravděpodobně způsobily miliardu dolarů bolesti a škod.

Zajímavá věc je, že Tony byl v pokušení tento odkaz implementovat, protože to bylo snadné. Ale proč vůbec potřeboval takový odkaz?

Různé významy NULL

V dokonalém světě bychom nepotřebovali NULL. Každý člověk má křestní jméno a příjmení. Každý člověk má datum narození, zaměstnání atd. Nebo ano?

Bohužel ne.

Ne všechny země používají koncept jména a příjmení.

Ne všichni lidé mají práci. Nebo někdy neznáme jejich práci. Nebo je nám to jedno.

Zde je hodnota NULL mimořádně užitečná. NULL dokáže modelovat všechny tyto stavy, které ve skutečnosti modelovat nechceme. NULL může být:

  • Nedefinovaná hodnota , tj. hodnota, která ještě není definována (pravděpodobně z technických důvodů), ale může být definována později. Přemýšlejte o osobě, kterou chceme přidat do databáze, abychom ji mohli použít v jiných tabulkách. V pozdější fázi přidáme práci této osoby.
  • Hodnota „neznámá“ , tedy hodnotu, kterou neznáme (a možná nikdy nebudeme znát). Možná se již nemůžeme zeptat této osoby nebo jejích příbuzných na datum narození – informace budou navždy ztraceny. Ale stále chceme modelovat osobu, takže používáme NULL ve smyslu NEZNÁMÝ (což je jeho skutečný význam v SQL, jak uvidíme později).
  • Nepovinná hodnota , tedy hodnotu, kterou není třeba definovat. Všimněte si, že hodnota „nepovinná“ se objeví také v případě OUTER JOIN, kdy vnější spojení nevytváří žádné hodnoty na jedné straně vztahu. Nebo také při použití GROUPING SETS, kde jsou různé kombinace sloupců GROUP BY kombinovány (nebo ponechány prázdné).
  • Hodnota „smazáno“ nebo „vyhnuto“ , tedy hodnotu, kterou nechceme specifikovat. Možná obvykle registrujeme rodinný stav osoby, jak se to děje v některých jurisdikcích, ale ne v jiných, kde není legální registrovat jakékoli osobní údaje tohoto typu. Proto v některých případech tuto hodnotu znát nechceme.
  • „Speciální“ hodnota v daném kontextu , tedy hodnotu, kterou jinak v rozsahu možných hodnot modelovat neumíme. To se často provádí při práci s rozsahy dat. Předpokládejme, že práce osoby je omezena dvěma daty, a pokud tato osoba aktuálně pracuje na dané pozici, použijeme hodnotu NULL, abychom řekli, že období je na konci časového období neomezené.
  • „Náhodné“ NULL , tj. hodnota NULL, která je pouze NULL, protože tomu vývojáři nevěnovali pozornost. Při absenci explicitního omezení NOT NULL většina databází předpokládá, že sloupce mohou mít hodnotu null. A jakmile je u sloupců možné použít hodnotu null, vývojáři mohou jen „omylem“ zadat hodnoty NULL do svých řádků, kde to ani neměli v úmyslu.

Jak jsme viděli výše, toto je jen několik vybraných z 50 odstínů NULL .

Následující příklad zobrazuje různé různé významy NULL v konkrétním příkladu SQL:




CREATE TABLE company (
    id int NOT NULL,
    name text NOT NULL,
    CONSTRAINT company_pk PRIMARY KEY (id)
);
CREATE TABLE job (
    person_id int NOT NULL,
    start_date date NOT NULL,

    -- If end_date IS NULL, the “special value” of an unbounded
    -- interval is encoded
    end_date date NULL,
    description text NOT NULL,

    -- A job doesn’t have to be done at a company. It is “optional”.
    company_id int NULL,
    CONSTRAINT job_pk PRIMARY KEY (person_id,start_date),
    CONSTRAINT job_company FOREIGN KEY (company_id) 
        REFERENCES company (id) 
);
CREATE TABLE person (
    id int  NOT NULL,
    first_name text NOT NULL,

    -- Some people need to be created in the database before we
    -- know their last_names. It is “undefined”
    last_name text NULL,

    -- We may not know the date_of_birth. It is “unknown”
    date_of_birth date NULL,

    -- In some situations, we must not define any marital_status.
    -- It is “deleted”
    marital_status int NULL,
    CONSTRAINT person_pk PRIMARY KEY (id),
    CONSTRAINT job_person FOREIGN KEY (person_id)
        REFERENCES person (id)
); 

Lidé se vždy hádali o absenci hodnoty

Když je NULL tak užitečnou hodnotou, proč ji lidé neustále kritizují?

Všechny tyto předchozí případy použití pro NULL (a další) jsou zobrazeny v této zajímavé nedávné přednášce C. J. Date na téma „Problém chybějících informací“ (podívejte se na video na YouTube).

Moderní SQL dokáže spoustu úžasných věcí, o kterých málokdo z vývojářů univerzálních jazyků jako Java, C#, PHP neví. Níže vám ukážu příklad.

C.J. Date svým způsobem souhlasí s Tonym Hoareem, že (ne)používat NULL pro všechny tyto různé typy „chybějících informací“ je velmi špatná volba.

Například v elektronice se podobné techniky používají k modelování věcí jako 1, 0, „konflikt“, „nepřiřazeno“, „neznámo“, „nezajímá mě to“, „vysoká impedance“. Všimněte si však, jak v elektronice různé speciální hodnoty se pro tyto věci používají namísto jedné speciální hodnoty NULL . Je to opravdu lepší? Jak vnímají programátoři JavaScriptu rozdíl mezi různými „falešnými“ hodnotami, jako je „null“, „undefined“, „0“, „NaN“, prázdný řetězec ‚‘? Je to opravdu lepší?

Když už mluvíme o nule:Když na chvíli opustíme prostor SQL a pustíme se do matematiky, uvidíme, že staré kultury jako Římané nebo Řekové měli stejné problémy s číslem nula. Ve skutečnosti ani neměli žádný způsob, jak reprezentovat nulu na rozdíl od jiných kultur, jak lze vidět v článku Wikipedie o čísle nula. Citace z článku:

Záznamy ukazují, že staří Řekové si nebyli jisti stavem nuly jako čísla. Ptali se sami sebe:„Jak může nic být něčím?“, což vedlo k filozofickým a ve středověku i náboženským argumentům o povaze a existenci nuly a vakua.

Jak vidíme, „náboženské argumenty“ se jasně rozšiřují na počítačovou vědu a software, kde stále s jistotou nevíme, co dělat s absencí hodnoty.

Zpět do reality:NULL v SQL

I když se lidé (včetně akademiků) stále neshodnou na tom, zda potřebujeme nějaké kódování pro „nedefinované“, „neznámé“, „volitelné“, „smazané“, „speciální“, vraťme se zpět do reality a špatných částí SQL NULL.

Jedna věc, na kterou se při práci s NULL SQL často zapomíná, je to, že formálně implementuje případ UNKNOWN, což je speciální hodnota, která je součástí takzvané tříhodnotové logiky, a činí tak nekonzistentně, např. v případě operací UNION nebo INTERSECT.

Pokud se vrátíme k našemu modelu:





Pokud například chceme najít všechny lidi, kteří nejsou registrováni jako manželé, intuitivně bychom rádi napsali následující prohlášení:

SELECT * FROM person WHERE marital_status != 'married'

Bohužel, kvůli trojhodnotové logice a NULL SQL výše uvedený dotaz nevrátí ty hodnoty, které nemají žádný explicitní marital_status. Proto budeme muset napsat další, explicitní predikát:

SELECT * FROM person 
WHERE marital_status != 'married'
OR marital_status IS NULL

Nebo ji před porovnáním vynutíme na nějakou NENULL hodnotu

SELECT * FROM person
WHERE COALESCE(marital_status, 'null') != 'married'

Logika tří hodnot je těžká. A není to jediný problém s NULL v SQL. Zde jsou další nevýhody použití NULL:

  • Je pouze jedna hodnota NULL, když jsme skutečně chtěli zakódovat několik různých „nepřítomných“ nebo „speciálních“ hodnot. Rozsah užitečných speciálních hodnot velmi závisí na doméně a použitých typech dat. Ke správné interpretaci významu sloupce s možnou hodnotou null je však vždy zapotřebí znalost domény a dotazy musí být navrženy pečlivě, aby se zabránilo vracení nesprávných výsledků, jak jsme viděli výše.
  • Opakuji, že je velmi těžké najít správnou logiku se třemi hodnotami. I když je výše uvedený příklad stále poměrně jednoduchý, co si myslíte, že přinese následující dotaz?
    SELECT * FROM person 
    WHERE marital_status NOT IN ('married', NULL)
    

    Přesně tak. Nevynese to vůbec nic, jak je vysvětleno v tomto článku zde. Stručně řečeno, výše uvedený dotaz je stejný jako dotaz níže:

    SELECT * FROM person 
    WHERE marital_status != 'married'
    AND marital_status != NULL -- This is always NULL / UNKNOWN
    
  • Databáze Oracle považuje hodnotu NULL a prázdný řetězec '' za totéž. To je velmi složité, protože si okamžitě nevšimnete, proč následující dotaz vždy vrací prázdný výsledek:

    SELECT * FROM person 
    WHERE marital_status NOT IN ('married', '')
    

  • Oracle (opět) nedává hodnoty NULL do indexů. To je zdrojem mnoha nepříjemných problémů s výkonem, např. když používáte sloupec s možnou hodnotou Null v predikátu NOT IN jako takovém:

    SELECT * FROM person 
    WHERE marital_status NOT IN (
      SELECT some_nullable_column
      FROM some_table
    )
    

    U Oracle bude mít výše uvedené anti-join za následek úplné prohledání tabulky, bez ohledu na to, zda máte index na some_nullable_column. Kvůli tříhodnotové logice a protože Oracle nevkládá NULL do indexů, bude stroj muset zasáhnout tabulku a zkontrolovat každou hodnotu, aby se ujistil, že v sadě není alespoň jedna hodnota NULL, což by způsobilo, že celý predikát NEZNÁMÝ.

Závěr

Ve většině jazyků a platforem jsme zatím nevyřešili problém NULL. I když tvrdím, že NULL NENÍ chybou miliardy dolarů, za kterou se Tony Hoare snaží omluvit, NULL má rozhodně k dokonalosti také daleko.

Pokud chcete zůstat na bezpečné straně s návrhem databáze, vyhněte se za každou cenu NULL, pokud nezbytně nepotřebujete jednu z těchto speciálních hodnot pro kódování pomocí NULL. Pamatujte, že tyto hodnoty jsou:„undefined“, „unknown“, „optional“, „deleted“ a „special“ a další:The 50 Shades of NULL . Pokud se v takové situaci nenacházíte, vždy ve výchozím nastavení přidejte do každého sloupce v databázi omezení NOT NULL. Váš design bude mnohem čistší a váš výkon mnohem lepší.

Pokud by v DDL bylo výchozí pouze NOT NULL a klíčové slovo NULLABLE, které je třeba nastavit explicitně…

Jaké jsou vaše postřehy a zkušenosti s NULL? Jak by podle vás fungoval lepší SQL?

Lukas Eder je zakladatelem a generálním ředitelem společnosti Data Geekery GmbH se sídlem v Curychu ve Švýcarsku. Data Geekery prodává databázové produkty a služby kolem Javy a SQL od roku 2013.

Už od magisterského studia na EPFL v roce 2006 ho fascinovala interakce Java a SQL. Většinu těchto zkušeností získal v oblasti švýcarského E-Banking prostřednictvím různých variant (JDBC, Hibernate, většinou s Oracle). O tyto znalosti se rád podělí na různých konferencích, džbánech, interních prezentacích a na svém firemním blogu.


  1. Změna hesla při přihlášení SA na SQL Server (příklad T-SQL)

  2. SQL:výběr řádků, kde se hodnota sloupce změnila oproti předchozímu řádku

  3. Úvod do speciálních dotazů

  4. Tipy, jak přesunout databázi SQL Server z jednoho serveru na druhý - SQL výuka od Rajana Singha