ORACLE nie zezwala na wartości NULL w żadnej z kolumn, które zawierają klucz podstawowy. Wydaje się, że to samo dotyczy większości innych systemów „na poziomie przedsiębiorstwa”.
Jednocześnie większość systemów dopuszcza również unikalne ograniczenia w kolumnach dopuszczających wartość zerową.
Dlaczego jest tak, że ograniczenia unikatowe mogą mieć wartości NULL, a klucze podstawowe nie? Czy istnieje jakiś fundamentalny logiczny powód, czy jest to raczej ograniczenie techniczne?
database
database-design
Roman Starkov
źródło
źródło
Odpowiedzi:
Klucze podstawowe służą do jednoznacznego identyfikowania wierszy. Odbywa się to poprzez porównanie wszystkich części klucza z danymi wejściowymi.
Z definicji NULL nie może być częścią udanego porównania. Nawet porównanie do samego siebie (
NULL = NULL
) nie powiedzie się. Oznacza to, że klucz zawierający NULL nie zadziała.Dodatkowo w kluczu obcym dozwolone jest NULL, aby oznaczyć opcjonalną relację. (*) Pozwolenie na to w PK również by to zepsuło.
(*) Uwaga: posiadanie kluczy obcych dopuszczających wartość null nie jest czystym projektem relacyjnej bazy danych.
Jeśli istnieją dwie jednostki
A
iB
gdzieA
można je opcjonalnie powiązaćB
, czystym rozwiązaniem jest utworzenie tabeli rozdzielczości (powiedzmyAB
). Że stół będzie połączyćA
zB
: Jeśli nie jest relacja wtedy ona zawierać zapis, jeśli nie jest , to nie będzie.źródło
Klucz podstawowy definiuje unikatowy identyfikator dla każdego wiersza w tabeli: jeśli tabela ma klucz podstawowy, masz gwarantowany sposób na wybranie dowolnego wiersza w tabeli.
Unikalne ograniczenie niekoniecznie identyfikuje każdy wiersz; po prostu określa, że jeśli wiersz zawiera wartości w swoich kolumnach, to muszą one być unikalne. To nie wystarczy, aby jednoznacznie zidentyfikować każdy wiersz, co musi zrobić klucz podstawowy.
źródło
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:
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:
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ślnyBOTTOM
typ,NULL
aby zapobiec nawykowi przypadkowego mieszania semantyki „brak wartości” z „nieznaną wartością”. ]źródło
NULL == NULL -> false (przynajmniej w DBMS)
Więc nie byłbyś w stanie pobrać żadnych relacji przy użyciu wartości NULL, nawet z dodatkowymi kolumnami z wartościami rzeczywistymi.
źródło
where pk_1 = 'a' and pk_2 = 'b'
z normalnymi wartościami i przełączyć się nawhere pk_1 is null and pk_2 = 'b'
gdy występują wartości null.where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))
/Odpowiedź Tony'ego Andrewsa jest przyzwoita. Ale prawdziwa odpowiedź jest taka, że była to konwencja używana przez społeczność relacyjnych baz danych i NIE jest to konieczność. Może to dobra konwencja, a może nie.
Porównanie czegokolwiek z wartością NULL daje wynik NIEZNANY (trzecia wartość prawdy). Tak więc, jak zasugerowano w przypadku wartości zerowych, cała tradycyjna mądrość dotycząca równości wychodzi przez okno. Cóż, tak to wygląda na pierwszy rzut oka.
Ale nie sądzę, żeby tak było i nawet bazy danych SQL nie uważają, że NULL niweczy wszelkie możliwości porównania.
Uruchom w bazie danych zapytanie SELECT * FROM VALUES (NULL) UNION SELECT * FROM VALUES (NULL)
To, co widzisz, to tylko jedna krotka z jednym atrybutem o wartości NULL. Tak więc unia rozpoznała tutaj dwie wartości NULL jako równe.
Podczas porównywania klucza złożonego, który ma 3 składniki, do krotki z 3 atrybutami (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 AND 3 = 3 AND NULL = NULL Wynik tego jest NIEZNANY .
Ale moglibyśmy zdefiniować nowy rodzaj operatora porównania, np. ==. X == Y <=> X = Y LUB (X JEST NULL AND Y JEST NULL)
Posiadanie tego rodzaju operatora równości sprawiłoby, że klucze złożone ze składnikami o wartości null lub klucz niezłożony o wartości null byłyby bezproblemowe.
źródło
Nadal uważam, że jest to fundamentalna / funkcjonalna wada spowodowana technicznością. Jeśli masz opcjonalne pole, za pomocą którego możesz zidentyfikować klienta, musisz teraz zhakować do niego wartość fikcyjną, tylko dlatego, że NULL! = NULL, niezbyt eleganckie, ale jest to „standard branżowy”
źródło