Przejdź do zawartości

Null (SQL)

To jest dobry artykuł
Z Wikipedii, wolnej encyklopedii
Małą grecką literą omega (ω) oznacza się Null w teorii baz danych

Null – specjalny znacznik w języku SQL, wskazujący, że dana nie istnieje w bazie danych. Wprowadzony przez twórcę relacyjnego modelu bazy danych, Edgara Franka Codda, znacznik Null istnieje, aby spełnić wymóg, że wszystkie prawdziwe systemy zarządzania relacyjną bazą danych udostępniają reprezentację „informacji brakującej i informacji niestosowalnej”. Codd zapoczątkował również przedstawianie znacznika Null w teorii baz danych za pomocą małej greckiej litery omega (ω). NULL to także zarezerwowane słowo w SQL, używane do identyfikacji specjalnego znacznika Null.

Znacznik Null był przedmiotem kontrowersji i powodem dyskusji z powodu powiązanej z nim logiki trójwartościowej, specjalnych wymagań wobec stosowania go w złączeniach i wymagania jego specjalnej obsługi w funkcjach agregujących i operatorach grupowania. Mimo wprowadzenia specjalnych funkcji i predykatów, aby właściwie obsługiwać znacznik Null, jego przeciwnicy uważają, że rozwiązywanie tych kwestii wprowadza niepotrzebne skomplikowanie i niekonsekwencję do relacyjnego modelu baz danych.

Historia

[edytuj | edytuj kod]

Null został wprowadzony przez Edgara Franka Codda jako sposób na przedstawienie brakujących danych w modelu relacyjnym. W późniejszym czasie Codd, w dwuczęściowej serii opublikowanej w czasopiśmie ComputerWorld[1][2], wzmocnił swoje wymagania pisząc, że wszystkie RDBMS powinny wspierać Null, aby wskazać brakujące dane. Codd wprowadził również trójwartościową logikę składającą się z wartości logicznych Prawda, Fałsz i Nieznany, które są ściśle związane z pojęciem Null. Wartość logiczna Nieznany jest uzyskiwana gdy Null jest porównana z dowolną daną lub innym Null.

Codd wskazał w swojej książce The Relational Model for Database Management, Version 2 z 1990 roku, że pojedynczy Null nakazany przez standard SQL jest niewystarczający i powinien być zastąpiony przez dwa wskaźniki typu Null, aby móc wskazać powód dlaczego danych brakuje. Te dwa znaczniki są powszechnie znane jako „wartości-A” i „wartości-I”, przedstawiające odpowiednio „brakujące, lecz dające się stosować” i „brakujące, lecz niewłaściwe”[3]. Zalecenia Codda wymagałoby rozszerzenia systemu logiki SQL, aby umieścić cztery zmienne logiczne. Z powodu dodatkowych komplikacji, pomysł wielu wartości typu Null nie uzyskał powszechnej akceptacji.

Logika trójwartościowa

[edytuj | edytuj kod]
 Osobny artykuł: Logika trójwartościowa.

Ponieważ Null nie jest członkiem jakiejkolwiek domeny danych, nie jest on rozważany jako „wartość”, lecz raczej jako znacznik (lub symbol zastępczy) wskazujący na brak wartości. Z uwagi na to, porównywanie z Null nigdy nie może zwrócić Prawdy lub Fałszu, lecz zawsze trzeci logiczny wynik, Nieznany[4]. Logiczny wynik poniższego wyrażenia, które porównuje wartość 10 z Null to Nieznany.

SELECT 10 = NULL       -- Wynik to Nieznany

Jednakże w pewnych operacjach z Null, można otrzymać wartości, jeśli Null nie ma wpływu na wyniki takiej operacji. Rozważając następujący przykład:

SELECT NULL OR TRUE   -- Wynik to Prawda

W tym przypadku, fakt, że wartość z lewej strony operatora sumy logiczniej (OR) jest nieznana jest bez znaczenia, ponieważ wynikiem tej sumy może być Prawda, niezależnie od wartości z lewej strony.

SQL realizuje trzy wyniki logiczne, czyli implementacje SQL muszą zapewnić specjalną logikę trójwartościową. Zasady rządzące trójwartościową logiką SQL są przedstawione w tabelach poniżej (p i q reprezentują stany logiczne)[5].

p q p LUB q p I q p = q
Prawda Prawda Prawda Prawda Prawda
Prawda Fałsz Prawda Fałsz Fałsz
Prawda Nieznany Prawda Nieznany Nieznany
Fałsz Prawda Prawda Fałsz Fałsz
Fałsz Fałsz Fałsz Fałsz Prawda
Fałsz Nieznany Nieznany Fałsz Nieznany
Nieznany Prawda Prawda Nieznany Nieznany
Nieznany Fałsz Nieznany Fałsz Nieznany
Nieznany Nieznany Nieznany Nieznany Nieznany
p NIE p
Prawda Fałsz
Fałsz Prawda
Nieznany Nieznany

Tablica prawdy trójwartościowej logiki SQL

Podstawowe operatory porównujące w SQL zawsze zwracają Nieznany kiedy porównywane jest cokolwiek z Null, wobec czego standard SQL dostarcza dwa specjalne i specyficzne dla Null predykaty porównujące. Predykaty IS NULL i IS NOT NULL sprawdzają czy dana jest, albo nie jest, Null[6].

Typowanie danych

[edytuj | edytuj kod]

W SQL Null nie ma typu, oznacza to, że nie jest on oznaczony jako liczba, znak lub jakikolwiek inny określony typ danych[4]. Z tego powodu, czasami jest wymagane (lub pożądane) jawne konwertowanie Null do określonego typu danych. Na przykład, jeśli RDBMS wspiera przeciążanie funkcji, SQL może nie być w stanie automatycznie dowiązać właściwych funkcji bez znajomości typów danych wszystkich parametrów, włączając w nie te, które zostały przekazane jako Null.

Język przetwarzania danych

[edytuj | edytuj kod]

Trójwartościowa logika SQL występuje w języku przetwarzania danych (DML) w predykatach porównujących, w wyrażeniach i zapytaniach DML. Klauzula WHERE powoduje, że wyrażenie DML działa tylko na wierszach dla których predykat zwraca Prawda. Wiersze, dla których predykat zwraca Fałsz lub Nieznany nie biorą udziału w wyrażeniach DML INSERT, UPDATE lub DELETE, oraz są pomijane w zapytaniach SELECT. Interpretowanie Nieznany i Fałsz jako takich samych logicznych wyników to powszechny błąd występujący przy działaniach z Null[5]. Następujący prosty przykład demonstruje taką omyłkę:

SELECT * 
FROM t 
WHERE i = NULL;

Powyższy przykład zapytania logicznie zawsze zwraca zero wierszy ponieważ porównanie kolumny i z Null zawsze zwraca Nieznany, nawet dla wierszy w których i jest Null. Wynik Nieznany powoduje, że zapytanie SELECT szybko pominie wszystkie wiersze. (Chociaż, w praktyce, niektóre narzędzia SQL zwrócą wiersze stosując porównanie z Null.)

Wyrażenie CASE

[edytuj | edytuj kod]

Wyrażenie SQL CASE dla Null działa na takich samych zasadach jak klauzula DML WHERE. Ponieważ może ono być rozwijane jako szereg warunków porównujących, proste wyrażenie CASE może sprawdzić wystąpienia Null bezpośrednio. Test na Null w prostym wyrażeniu CASE zawsze zwraca Nieznany, jak poniżej:

SELECT CASE i WHEN NULL THEN 'jest Null'  -- To nigdy nie będzie zwrócone
              WHEN    0 THEN 'jest zero'  -- To będzie zwrócone kiedy i = 0
              WHEN    1 THEN 'jest jeden' -- To będzie zwrócone kiedy i = 1
              END
FROM t;

Ponieważ wyrażenie i = NULL jest rozwijane do Nieznany jest bez znaczenia jaką wartość ma kolumna i (nawet jeśli zawiera Null), tekst 'jest Null' nigdy nie będzie zwrócony.

Przeszukujące wyrażenie CASE także zwraca pierwszą wartość dla której wynik predykatu porównującego rozwija się do Prawda, włączając porównania stosujące predykaty IS NULL i IS NOT NULL. Następujący przykład pokazuje jak stosować przeszukujące wyrażenie CASE aby właściwie sprawdzać Null:

SELECT CASE WHEN i IS NULL THEN 'wynik Null'  -- To jest zwrócone kiedy i jest NULL
            WHEN     i = 0 THEN 'zero'        -- To jest zwrócone kiedy i = 0
            WHEN     i = 1 THEN 'jeden'       -- To jest zwrócone kiedy i = 1
            END
FROM t;

W przeszukującym wyrażeniu CASE, tekst 'wynik Null' jest zwrócone dla wszystkich wierszy w których i jest Null.

Ograniczenia definiowane

[edytuj | edytuj kod]

Główne miejsce gdzie logika trójwartościowa krzyżuje się z językiem definicji danych (DDL) to zdefiniowane ograniczenia. Definiowane ograniczenie umieszczone na kolumnie działa pod nieco zmienionymi regułami, niż klauzula DML WHERE. Podczas gdy klauzula DML WHERE musi być rozwinięta do Prawda dla wiersza, ograniczenie definiowane nie może się rozwinąć do Fałsz. To znaczy, że ograniczenie definiowane powiedzie się jeśli wynik to Prawda lub Nieznany. Następująca przykładowa tabela ze zdefiniowanym ograniczeniem zabrania wstawienia każdej liczby całkowitej, ale pozwala na wstawienie Null, gdyż wynik sprawdzenia zawsze jest rozwijany do Nieznany dla Null[7].

CREATE TABLE t (
     i INTEGER,
     CONSTRAINT ck_i CHECK ( i < 0 AND i = 0 AND i > 0 ) );

Aby wymusić ograniczenie na kolumnie aby nie akceptowała Nulli, można zastosować ograniczenie NOT NULL, jak to pokazano na przykładzie poniżej. Ograniczenie NOT NULL jest semantycznie równoważne z ograniczeniem definiowanym z predykatem IS NOT NULL.

CREATE TABLE t ( i INTEGER NOT NULL );

Rozszerzenia proceduralne

[edytuj | edytuj kod]

SQL/PSM (ang. SQL Persistent Stored Modules – trwale przechowywane moduły) definiuje proceduralne rozszerzenia dla SQL, takie jak wyrażenie IF. Jednakże, główni dostawcy SQL historycznie zawierają swoje własne zastrzeżone rozszerzenia proceduralne. Proceduralne rozszerzenia dla pętli i porównań działają według reguł podobnych do zasad z wyrażeń i zapytań DML. Następujący fragment kodu, w standardzie ISO SQL, demonstruje użycie trójwartościowej logiki Null z wyrażeniem IF.

IF i = NULL THEN
      SELECT 'wynik to Prawda'
ELSEIF NOT(i = NULL) THEN
      SELECT 'wynik to Fałsz'
ELSE
      SELECT 'wynik to Nieznany';

Wyrażenie IF wykonuje akcję tylko dla tych porównań, które rozwijają się do Prawda. W wyrażeniach, w których się rozwija do Fałsz lub Nieznany, wyrażenie IF przekazuje sterowanie do klauzuli ELSEIF, a na końcu do klauzuli ELSE. Wynikiem kodu powyżej będzie zawsze wiadomość 'wynik to Nieznany' ponieważ porównania z Null zawsze zwracają Nieznany.

Złączenia

[edytuj | edytuj kod]

Złączenia zewnętrzne, tj. lewe złączenie zewnętrzne, prawe złączenie zewnętrzne i pełne złączenie zewnętrzne, automatycznie umieszczają Null jako zamiennik dla wartości brakujących w powiązanych tabelach. Na przykład w lewym złączeniu zewnętrznym, Null jest umieszczany w miejscu brakujących wierszy z tabeli, która pojawia się po prawej stronie operatora LEFT OUTER JOIN. Następujący przykład używa dwóch tabel aby zademonstrować generowanie Null w lewym zewnętrznym złączeniu.

Pierwsza tabela (Pracownik) zawiera identyfikatory i nazwy pracowników, natomiast druga tabela (NumerTelefonu) zawiera powiązane z identyfikatorami pracowników numery telefonów, jak następuje.

Pracownik
ID Nazwisko Imię
1 Jankowski Jacek
2 Krawiec Ludwik
3 Tomaszewski Tomasz
4 Pawlikowska Patrycja
NumerTelefonu
ID Numer
1 555-2323
3 555-9876

Następujący przykład zapytania SQL wykonuje lewe zewnętrzne złączenie na obu tych tabelach.

SELECT p.ID, p.Nazwisko, p.Imię, nt.Numer
FROM Pracownik p
LEFT OUTER JOIN NumerTelefonu nt
ON p.ID = nt.ID;

Zbiór danych uzyskanych dzięki temu zapytaniu ukazuje jak SQL stosuje Null jako zamiennik dla brakujących danych z prawostronnej tabeli (NumerTelefonu), jak następuje.

Wynik zapytania
ID Nazwisko Imię Numer
1 Jankowski Jacek 555-2323
2 Krawiec Ludwik NULL
3 Tomaszewski Tomasz 555-9876
4 Pawlikowska Patrycja NULL

W SQL istnieją również złączenia wewnętrzne i krzyżujące, które nie generują zamienników Null dla brakujących danych w powiązanych tabelach.

Należy zwrócić szczególną uwagę, w przypadkach gdy kolumny używane jako kryteria w złączeniach mogą zawierać Null. Ponieważ Null nie równa się jakiemukolwiek innemu Null, Nulle z jednej tabeli nie połączą się z Nullami z powiązanej kolumny z innej tabeli przy stosowaniu standardowych operatorów równości. W takich przypadkach przydatna jest funkcja SQL COALESCE lub wyrażenie CASE do „symulowania” równości z Null w kryteriach złączenia, jak również można stosować predykaty IS NULL lub IS NOT NULL.

Następujący predykat sprawdza równość wartości A i B, i traktuje Nulle jakby były równe. Operator IFNULL jest wymagany ponieważ A = B zwraca Null jeśli przynajmniej jeden z argumentów A lub B jest Null a NULL OR FALSE samo z siebie zwraca Null.

IFNULL( A = B, FALSE ) OR ( A IS NULL AND B IS NULL )

Operacje matematyczne i konkatenacja tekstów

[edytuj | edytuj kod]

Ponieważ Null nie jest wartością danych, lecz raczej znacznikiem dla wartości nieznanej, używanie operatorów matematycznych na Null skutkuje wartością nieznaną, która jest reprezentowana przez Null[8]. W następującym przykładzie, mnożenie 10 przez Null daje w wyniku Null:

10 * NULL          -- Wynik to NULL

Może to prowadzić do nieoczekiwanych wyników. Na przykład, podczas próby dzielenia Null przez zero, platforma może zwrócić Null zamiast zgłosić oczekiwany wyjątek „dzielenie przez zero[8]. Chociaż takie zachowanie nie jest zdefiniowane przez standard ISO SQL, wiele dostawców DBMS traktuje tę operację podobnie. Na przykład wszystkie platformy Oracle, PostgreSQL, MySQL Server, i Microsoft SQL Server zwracają wynik Null dla następującego wyrażenia:

NULL / 0

Operacje konkatenacji tekstów, które są powszechne w SQL, także zwracają Null, kiedy jednym z argumentów jest Null[9]. Następujący przykład demonstruje wynik Null zwrócony przez operator konkatenacji tekstów ||.

'kawałki ' || NULL || 'ryby'   -- Wynikiem jest NULL

Nie jest to prawda we wszystkich implementacjach baz danych. Na przykład w RDBMS Oracle, NULL i pusty tekst są uznawane za tożsame i stąd 'kawałki ' || NULL || 'ryby' daje w wyniku 'kawałki ryby'.

Funkcje agregujące

[edytuj | edytuj kod]

SQL definiuje funkcje agregujące aby uprościć obliczenia agregujące na danych po stronie serwera. Z wyjątkiem funkcji COUNT(*), wszystkie funkcje agregujące wykonują krok eliminacji Null tak, że wartości Null nie są uwzględniane w ostatecznym wyniku obliczeń[10]

Należy podkreślić, że eliminacja wartości Null nie oznacza zastąpienie tych wartości przez zero. Na przykład, w poniższej tabeli, AVG(i) (średnia z wartości z i) zwróci inne wyniki niż AVG(j):

Tabela
i j
150 150
200 200
250 250
NULL 0

W tym przypadku AVG(i) to 200 (średnia ze 150, 200 i 250), podczas gdy AVG(j) to 150 (średnia ze 150, 200, 250 i 0).

Grupowanie i sortowanie

[edytuj | edytuj kod]

Ponieważ SQL 2003 definiuje, że każdy znacznik Null jest różny od każdego innego, nastąpiła potrzeba specjalnej definicji pozwalającej na grupowanie wartości Null przy wykonywaniu pewnych operacji. SQL definiuje „dowolne dwie wartości które są równe sobie lub dowolne dwa Null” jako „nieodrębne”[11]. Taka definicja nieodrębności pozwala w SQL na grupowanie i sortowanie Nulli kiedy stosowana jest klauzula GROUP BY (i inne słowa kluczowe które dokonują grupowania).

Inne operacje, klauzule i słowa kluczowe SQL, które stosują „nieodrębne” traktowanie Nulli to:

  • PARTITION BY klauzula zaszeregowania i funkcje okienkowe jak ROW_NUMBER
  • operatory UNION, INTERSECT i EXCEPT, które traktują NULLe jako takie same w celu porównania/eliminacj wierszy
  • słowo kluczowe DISTINCT stosowane w zapytaniach SELECT

SQL nie definiuje wprost domyślnego porządku sortowania dla Null. W zamian, na systemach zgodnych, Null może być sortowane przed lub po wartościach danych za pomocą klauzul NULLS FIRST lub NULLS LAST odpowiednio w wyrażeniu ORDER BY. Jednakże nie wszyscy dostawcy DBMS implementują tę funkcjonalność. Dostawcy, którzy nie implementują tej funkcjonalności, mogą określać różne traktowanie Null dla sortowania w DBMS[7].

Wpływ na indeksowanie

[edytuj | edytuj kod]

Niektóre produkty SQL nie indeksują kluczy z wartościami NULL. Na przykład PostgreSQL przed wersją 8.3 w dokumentacji dla indeksu o strukturze B-drzewa stwierdza, że planista zapytania SQL rozważa użycie indeksu gdy w zapytaniu są użyte operatory relacyjne (tj. < ≤ = ≥ >) oraz ich równoważne formy złożone (BETWEEN i IN), podkreślając, że IS NULL nie jest równoznaczne z = i nie podlega indeksowaniu[12]. W przypadku, gdy indeks wymusza unikatowość, wartości NULL są wykluczone z indeksu a unikatowość nie jest wymuszana pomiędzy wartościami NULL. Wartości NULL są w takim przypadku uznawane za różne. Jeśli unikatowy indeks zawiera więcej niż jedną kolumnę, to odrzucane są tylko takie przypadki, w których wszystkie kolumny mają takie same wartości[13]. Jest to spójne z definicją SQL:2003 dotyczącą zachowania skalarnego porównywania NULL.

Inna metoda indeksowania znaczników Null polega na traktowaniu ich jako nieodrębnych, w zgodzie z definicją zachowania w SQL:2003. Na przykład według dokumentacji do Microsoft SQL Server wartości NULL w kolumnach indeksowanych są traktowane jako równe i zalecane jest tworzenie indeksów, lub ograniczeń UNIQUE na kolumnach, w których NULL nie jest dozwolone[14].

Obie te strategie są spójne ze zdefiniowanym w SQL:2003 zachowaniem dla znaczników Null. Ponieważ metodologie indeksowania nie są zdefiniowane przez standard SQL:2003, kwestie indeksowania Null, zarówno projekt jak i implementacja, są pozostawione całkowicie gestii dostawców.

Funkcje obsługujące Null

[edytuj | edytuj kod]

SQL definiuje dwie funkcje do jawnej obsługi Null: NULLIF i COALESCE. Obie funkcje są skrótem dla przeszukującego wyrażenia CASE[15].

NULLIF

[edytuj | edytuj kod]

Funkcja NULLIF przyjmuje dwa parametry. Jeśli pierwszy parametr jest równy drugiemu parametrowi to NULLIF zwraca Null. W przeciwnym razie zwracana jest wartość pierwszego parametru.

NULLIF(parametr1, parametr2)

Tak więc, NULLIF to skrót dla następującego wyrażenia CASE:

CASE WHEN parametr1 = parametr2 THEN NULL ELSE parametr1 END

COALESCE

[edytuj | edytuj kod]

Funkcja COALESCE przyjmuje listę parametrów a zwraca pierwszy parametr z listy, który nie jest Null:

COALESCE(parametr1, parametr2, parametr3, ...)

COALESCE jest zdefiniowana jako skrót dla następującego wyrażenia SQL CASE:

CASE WHEN parametr1 IS NOT NULL THEN parametr1
     WHEN parametr2 IS NOT NULL THEN parametr2
     WHEN parametr3 IS NOT NULL THEN parametr3
     ...
     END

Niektóre DBMS SQL implementują specyficzne dla dostawcy funkcje podobne do COALESCE. Niektóre systemy (np. Transact-SQL) implementują funkcję ISNULL, lub inne podobne funkcje, które są funkcjonalnie podobne do COALESCE.

Kontrowersje

[edytuj | edytuj kod]

Powszechne błędy

[edytuj | edytuj kod]

Niezrozumienie zasad działania Null jest powodem dużej liczby błędów w kodzie SQL, zarówno w wyrażeniach zgodnych ze standardem ISO SQL jak również w dialektach wspieranych przez rzeczywiste systemy zarządzania bazami danych. Błędy te są zwykle wynikiem pomylenia Null z 0 (zerem) bądź pustym tekstem (wartością tekstową o długości zero, przedstawionej w zapisie SQL jako ''). Jednak Null jest zdefiniowany przez standard ISO SQL jako różny od obu, pustego tekstu i wartości liczbowej 0. Podczas gdy Null wskazuje na brak danych dowolnej wartości, pusty tekst i liczba zero przedstawiają właściwe wartości.

Na przykład klauzula WHERE lub wyrażenie warunkowe może porównywać wartość kolumny ze stałą. Często dochodzi do nieprawidłowego założenia, że wartość brakująca może być „mniejsza niż” albo „różna od” stałej jeśli pole zawiera Null, lecz w rzeczywistości takie wyrażenia zwracają Nieznany. Przykład jest poniżej:

SELECT *
FROM jakaśtabela
WHERE kolumna <> 1;  -- Wiersze gdzie kolumna jest NULL nie będą zwracane, 
                     -- w przeciwieństwie do oczekiwań wielu użytkowników.

Podobnie, wartości Null są często mylone z pustymi tekstami. Rozważając funkcję LENGTH, która zwraca liczbę znaków w tekście. Kiedy do funkcji zostaje przekazany parametr Null, to funkcja zwraca Null. Może to prowadzić do nieoczekiwanych wyników, jeśli użytkownicy nie są odpowiednio biegli w logice trójwartościowej. Przykład jest poniżej:

SELECT * 
FROM jakaśtabela
WHERE LENGTH(tekst) < 20; -- Wiersze gdzie tekst jest NULL nie będą zwracane.

Jest to dodatkowo skomplikowane z uwagi na to, że w niektórych programach interfejsu bazy danych, NULL jest zgłaszane jako pusty tekst, a puste teksty mogą być nieprawidłowo zapisywane jako NULL.

Krytyka

[edytuj | edytuj kod]

Realizacja Null w ISO SQL jest przedmiotem krytyki i dyskusji wzywającej do zmian. W The Relational Model for Database Management: Version 2, Codd sugeruje, że SQL'owa realizacja Null została źle wprowadzona i powinna być zastąpiona przez dwa różne znaczniki typu Null. Znaczniki, które zaproponował to brakujące, lecz dające się zastosować oraz brakujące, lecz nie dające się zastosować, znane jako wartości-A i wartości-I odpowiednio. Zalecenia Codda, gdyby je zaakceptować, wymagałyby wdrożenia czterowartościowej logiki w SQL[3]. Pojawiały się również sugestie aby dodać kolejne znaczniki Null do zalecanych przez Codda, aby wskazać więcej powodów „brakujących” danych, zwiększając złożoność logiki systemu SQL. W różnych czasach pojawiały się także propozycje aby wdrożyć możliwość definiowania różnych znaczników Null przez użytkownika. Z uwagi na złożoność obsługi Null i wymagań systemu logiki aby wspierać różnorakie znaczniki Null, żadna z propozycji nie uzyskała szerszej akceptacji.

Chris Date i Hugh Darwen, autorzy The Third Manifesto, zasugerowali, że realizacja Null w SQL jest wewnętrznie wadliwa i powinna być wyeliminowana w całości[16], wskazując na niespójności i błędy w realizacji obsługi Null w SQL (szczególnie w funkcjach agregujących) jako dowód, że cała koncepcja Null jest błędna i powinna być usunięta z modelu relacyjnego[17]. Inni, jak Fabian Pascal, wysunął przekonanie, że „sposób traktowania brakujących danych w obliczeniach nie podlega modelowi relacyjnemu”.

Hipoteza zamkniętego świata

[edytuj | edytuj kod]

Kolejnym punktem konfliktu na tle Null jest to, że narusza on model hipotezy zamkniętego świata relacyjnych baz danych przez wprowadzenie do niego hipotezy otwartego świata[18]. Hipoteza zamkniętego świata, w odniesieniu do baz danych, stanowi, że „wszystko co jest określone w bazie danych, bezpośrednio lub w sposób dorozumiany, to prawda; cała reszta to fałsz”[19]. Takie spojrzenie zakłada, że wiedza o świecie zawarta w bazie danych jest kompletna. Jednak Null działa według hipotezy otwartego świata, w której pewne elementy zapisane w bazie danych są uważane za nieznane, powodując, że wiedza zapisana w bazie danych jest niepełna.

Prawo wyłączonego środka

[edytuj | edytuj kod]

SQL pozwala na trzy logiczne wybory, prawda, fałsz i nieznany, co oznacza, że SQL jest zmuszony do ignorowania prawa wyłączonego środka. Upraszczając, prawo wyłączonego środka stanowi, że mając dany wynik logiczny, jego przeciwieństwo można uzyskać stosując operator logicznej negacji. Jednakże nie ma to zastosowania w SQL dla null. Zgodnie z regułami prawa wyłączonego środka, następujące wyrażenie logicznie można uprościć:

SELECT * FROM stuff WHERE ( x = 10 ) OR NOT ( x = 10 );

Prawo wyłączonego środka pozwala na uproszczenie predykatu WHERE, które można by zastąpić następującym wyrażeniem:

SELECT * FROM stuff;

To nie zadziała w SQL, gdyż kolumna x mogłaby zawierać null, co w wyniku skutkowałoby zwróceniem pewnych nowych wierszy.

Właściwie:

SELECT * FROM stuff;
-- jest (z uwagi na logikę trójwartościową) równoważne z:
SELECT * FROM stuff WHERE ( x = 10 ) OR NOT ( x = 10 ) OR x IS NULL;

Stąd, aby prawidłowo uprościć pierwsze wyrażenie w SQL jest wymagane, że zwrócone będą wszystkie wiersze w których x nie jest null.

SELECT * FROM stuff WHERE x IS NOT NULL;

Podczas gdy ignorowanie prawa wyłączonego środka wiąże się z dodatkowym skomplikowaniem logiki SQL, próby zastosowania tej reguły w trójwartościowej logice SQL prowadzą do dychotomii myślenia.

Niespójności w logicznym typie danych

[edytuj | edytuj kod]

Standard ISO SQL:1999 wprowadził logiczny typ danych do trójwartościowej logiki SQL. Logiczny typ danych, jak określono w normie, może przechowywać wartości logiczne PRAWDA, FAŁSZ i NIEZNANY. Null jest zdefiniowany w tym jednym przypadku jako równoważny wartości logicznej NIEZNANY.

Propozycja, że „null równa się wartości logicznej NIEZNANY” wprowadza niespójność w trójwartościowej logice SQL. Jednym z głównych problemów jest to, że zaprzecza on podstawowej własności null, mianowicie propagacji. Null, z definicji, jest propagowane przez wszystkie wyrażenia SQL. Wartości logiczne tej własności nie mają. Rozważając następujące scenariusze w SQL:1999, w których dwie wartości logiczne są połączone w złożony predykat, zachodzą następujące wyrażenia (zgodnie z regułami trójwartościowej logiki SQL, jak to jest przedstawione w tabeli prawdy na początku artykułu):

  • ( PRAWDA LUB NIEZNANY ) → PRAWDA
  • ( FAŁSZ I NIEZNANY ) → FAŁSZ

Jednakże, z uwagi na propagację null, traktowanie null jako NIEZNANY skutkuje następującymi niespójnościami w trójwartościowej logice SQL:

  • ( TRUE OR NULL )NULL ( = NIEZNANY )
  • ( FALSE AND NULL )NULL ( = NIEZNANY )

Standard SQL:1999 nie definiuje jak obsługiwać te niespójności, a wyniki mogą być różne, zależnie od implementacji. Z powodu tych niespójności i brakiem wsparcia od producentów systemów bazodanowych, logiczny typ danych w SQL nie uzyskał szerszej akceptacji. Obecnie większość platform SQL DBMS oferuje własne (i specyficzne) zalecenia dla przechowywania danych typu logicznego.

Należy zauważyć, że w PostgreSQL, wartość null jest stosowana do przedstawiania wszystkich wyników NIEZNANY oraz zachodzą następujące zależności:

  • ( TRUE OR NULL )TRUE
  • ( FALSE AND NULL )FALSE
  • ( FALSE OR NULL ) IS NULLTRUE
  • ( TRUE AND NULL ) IS NULLTRUE

MySQL zachowuje się w tej kwestii podobnie do PostgreSQL (z małym wyjątkiem, że MySQL traktuje wartość TRUE tak samo jak liczbę 1 a wartość FALSE jak liczbę 0).

Przypisy

[edytuj | edytuj kod]
  1. Edgar Frank Codd, Is Your Database Really Relational?, „ComputerWorld”, 14 października 1985 (ang.). – Czy twoja baza jest naprawdę relacyjna?
  2. Edgar Frank Codd, Does Your DBMS Run By The Rules?, „ComputerWorld”, 21 października 1985 (ang.). – Czy twój system zarządzania bazą danych działa zgodnie z zasadami?
  3. a b Edgar Frank Codd, The Relational Model for Database Management, wyd. Version 2, Addison Wesley Publishing Company, 1990, ISBN 0-201-14192-2 (ang.).
  4. a b ISO/IEC 9075-1:2003 ↓, Section 4.4.2: The null value.
  5. a b Michael Coles, Four Rules for Nulls, „SQL Server Central”, Red Gate Software, 27 czerwca 2005 (ang.).
  6. ISO/IEC 9075-2:2003 ↓, Section 8.7: null predicate.
  7. a b Michael Coles, Null Versus Null?, „SQL Server Central”, Red Gate Software, 26 lutego 2007 (ang.).
  8. a b ISO/IEC 9075-2:2003 ↓, Section 6.2.6: numeric value expressions.
  9. ISO/IEC 9075-2:2003 ↓, Section 6.2.8: string value expression.
  10. ISO/IEC 9075-2:2003 ↓, Section 4.15.4: Aggregate functions.
  11. ISO/IEC 9075-2:2003 ↓, Section 3.1.6.8: Definitions: distinct.
  12. PostgreSQL 8.0.14 Documentation: Index Types [online], PostgreSQL [dostęp 2012-08-27], Cytat: B-trees can handle equality and range queries on data that can be sorted into some ordering. In particular, the PostgreSQL query planner will consider using a B-tree index whenever an indexed column is involved in a comparison using one of these operators: < ≤ = ≥ >
    Constructs equivalent to combinations of these operators, such as BETWEEN and IN, can also be implemented with a B-tree index search. (But note that IS NULL is not equivalent to = and is not indexable.) (ang.).
  13. PostgreSQL 8.0.14 Documentation: Unique Indexes [online], PostgreSQL [dostęp 2012-08-27], Cytat: When an index is declared unique, multiple table rows with equal indexed values will not be allowed. Null values are not considered equal. A multicolumn unique index will only reject cases where all of the indexed columns are equal in two rows. (ang.).
  14. Creating Unique Indexes, [w:] MSDN [online], Microsoft, wrzesień 2007 [dostęp 2012-08-27], Cytat: Handling NULL Values
    For indexing purposes, NULL values compare as equal. Therefore, a unique index, or UNIQUE constraint, cannot be created if the key values are NULL in more than one row. Select columns that are defined as NOT NULL when columns for a unique index or unique constraint are chosen. (ang.).
  15. ISO/IEC 9075-2:2003 ↓, Section 6.11: case expression.
  16. Hugh Darwen, Chris Date, The Third Manifesto [online] [dostęp 2012-09-04] (ang.).
  17. Hugh Darwen, The Askew Wall [online] [dostęp 2012-09-04] [zarchiwizowane z adresu 2016-03-13] (ang.).
  18. Chris Date, Database in Depth: Relational Theory for Practitioners, O'Reilly Media, Inc., maj 2005, s. 73, ISBN 0-596-10012-4.
  19. Chris Date, Abstract: The Closed World Assumption [online], Data Management Association, San Francisco Bay Area Chapter [dostęp 2007-05-29] [zarchiwizowane z adresu 2007-05-19] (ang.).

Bibliografia

[edytuj | edytuj kod]
  • ISO/IEC, ISO/IEC 9075-1:2003, "SQL/Framework", ISO/IEC, 2003 (ang.).
  • ISO/IEC, ISO/IEC 9075-2:2003, "SQL/Foundation", ISO/IEC, 2003 (ang.).

Linki zewnętrzne

[edytuj | edytuj kod]
  • Fabian Pascal, DATABASE DEBUNKINGS [online] [dostęp 2012-09-11] [zarchiwizowane z adresu 2011-07-16] (ang.).
E.F. Codd, C.J. Date, MUCH ADO ABOUT NOTHING [online] [dostęp 2012-09-11] [zarchiwizowane z adresu 2010-05-31] (ang.).Wiele hałasu o nic