Zasadniczo nie ma nic złego w NULL w wielokolumnowym kluczu podstawowym. Ale posiadanie takiego ma konsekwencje, których projektant prawdopodobnie nie zamierzał, dlatego wiele systemów wyświetla błąd podczas próby.
Rozważmy przypadek wersji modułu / pakietu przechowywanych jako seria pól:
CREATE TABLE module
(name varchar(20) PRIMARY KEY,
description text DEFAULT '' NOT NULL);
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20),
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
Pierwszych 5 elementów klucza podstawowego to regularnie definiowane części wersji wydania, ale niektóre pakiety mają dostosowane rozszerzenie, które zwykle nie jest liczbą całkowitą (np. „Rc-foo”, „vanilla”, „beta” lub cokolwiek innego dla których cztery pola są niewystarczające, może wymarzyć) Jeśli pakiet nie ma rozszerzenia, to w powyższym modelu ma wartość NULL i pozostawienie rzeczy w ten sposób nie byłoby szkodliwe.
Ale co to jest NULL? Ma reprezentować brak informacji, nieznane. To powiedziawszy, być może ma to większy sens:
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20) DEFAULT '' NOT NULL,
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
W tej wersji część „ext” krotki NIE ma wartości NULL, ale domyślnie jest to pusty ciąg - który jest semantycznie (i praktycznie) różny od wartości NULL. Wartość NULL jest nieznana, podczas gdy pusty łańcuch to celowy zapis „czegoś nieobecnego”. Innymi słowy, „pusty” i „pusty” to różne rzeczy. To różnica między „Nie mam tutaj wartości” a „Nie wiem, jaka jest tutaj wartość”.
Kiedy rejestrujesz pakiet, który nie ma rozszerzenia wersji, wiesz, że brakuje mu rozszerzenia, więc pusty ciąg jest w rzeczywistości poprawną wartością. Wartość NULL byłaby poprawna tylko wtedy, gdybyś nie wiedział, czy ma rozszerzenie, czy nie, lub wiedziałeś, że ma, ale nie wiesz, co to jest. Ta sytuacja jest łatwiejsza do rozwiązania w systemach, w których wartości łańcuchowe są normą, ponieważ nie ma innego sposobu na przedstawienie „pustej liczby całkowitej” niż wstawienie 0 lub 1, która zakończy się zawijaniem w późniejszych porównaniach (co ma własne implikacje) *.
Nawiasem mówiąc, oba sposoby są poprawne w Postgres (ponieważ omawiamy "korporacyjne" RDMBS), ale wyniki porównania mogą się znacznie różnić, gdy dodasz NULL do miksu - ponieważ NULL == "nie wiem", więc wszystko wyniki porównania z wartością NULL kończą się wartością NULL, ponieważ nie możesz wiedzieć czegoś, co jest nieznane. ZAGROŻENIE! Zastanów się dokładnie: oznacza to, że wyniki porównań NULL są propagowane przez serię porównań. Może to być źródłem subtelnych błędów podczas sortowania, porównywania itp.
Postgres zakłada, że jesteś osobą dorosłą i możesz samodzielnie podjąć tę decyzję. Oracle i DB2 zakładają, że nie zdawałeś sobie sprawy, że robisz coś głupiego i zgłaszasz błąd. Jest to zazwyczaj słuszne, ale nie zawsze - ty może rzeczywiście nie wiedzą i mają wartość null w niektórych przypadkach, a zatem pozostawiając wiersz z nieznanym elemencie przeciwko którym sensowne porównania są niemożliwe jest poprawne zachowanie.
W każdym przypadku powinieneś dążyć do wyeliminowania liczby pól NULL, na które zezwalasz w całym schemacie, a także podwójnie, jeśli chodzi o pola, które są częścią klucza podstawowego. W zdecydowanej większości przypadków obecność kolumn NULL jest oznaką nieznormalizowanego (w przeciwieństwie do celowo zdenormalizowanego) schematu i należy się nad nim bardzo intensywnie przemyśleć, zanim zostanie zaakceptowany.
[* UWAGA: Możliwe jest utworzenie niestandardowego typu będącego połączeniem liczb całkowitych i typu „dolnego”, który semantycznie oznaczałby „pusty” w przeciwieństwie do „nieznanego”. Niestety, wprowadza to trochę złożoności w operacjach porównawczych i zazwyczaj bycie naprawdę poprawnym względem typu nie jest warte wysiłku w praktyce, ponieważ w ogóle nie powinno się pozwalać na wiele NULL
wartości. To powiedziawszy, byłoby wspaniale, gdyby systemy RDBMS zawierały domyślny BOTTOM
typ, NULL
aby zapobiec nawykowi przypadkowego mieszania semantyki „brak wartości” z „nieznaną wartością”. ]