Kurs SQL #05: Wartość NULL i Three-Valued Logic

W tej części Darmowego Kursu SQL pokaże Ci czym jest wartość NULL. Dowiesz się na co zwrócić uwagę przy interpretacji wyników zapytań, które pobierają wiersze z taką wartością. Dodatkowo poznasz koncepcje Three-Valued Logic i potrzebne funkcje SQL!

Jesteś tutaj pierwszy raz? Nie widziałeś poprzednich lekcji? Zajrzyj tutaj: Kurs SQL #01: Wprowadzenie i środowisko

eBook Niezbędnik Administratora Baz Danych cover

Czym jest NULL?

Do tej pory pokazałem Ci zapytania pobierające dane z bazy. No właśnie… dane, a co się dzieje w bazach danych jak nie ma w nich danych? Wiem, brzmi to dziwacznie, ale taka sytuacja jest wbrew pozorom częsta. Jak dobrze już wiesz, dane przechowywane są w tabelach. Tabele z kolei składają się z zdefiniowanych już na etapie ich tworzenia kolumn. Zastanawiałeś się już może co się dzieję kiedy w danym wierszu, dla którejś z kolumn porostu nie ma danych? Sytuacje taką właśnie reprezentuje wartość NULL!

Standardowo najlepiej rozpoznawać zagadnienie na przykładzie, więc rzuć okiem na poniższe zapytanie.

select 
cust_id,cust_first_name, cust_last_name, cust_marital_status 
from sh.customers
order by 1;
CUST_IDCUST_FIRST_NAMECUST_LAST_NAMECUST_MARITAL_STATUS
1AbigailKessel
2AnneKoch
3BuickEmmerson
4FrankHardy
5MacklinGowen
6HarriettCharlessingle
7LinetteIngramsingle
8VidaPuleomarried
9RosamondKriderdivorced
10GertrudeAtkinswidow

Kolumna CUST_MARITAL_STATUS reprezentuje informacje na temat stanu cywilnego. Niektórzy z klientów nie podali swojego stanu cywilnego, dla nich właśnie wspomniana wcześniej kolumna ma wartość NULL. W przypadku Oracle Live SQL wartość ta pokazywana jest jako myślnik. Typowe narzędzia klienckie do bazy danych przeważnie pokazują wartość niewiadomą jako (null).

Gdybym miał powiedzieć w jednym zdaniu czym jest NULL, powiedziałbym że jest to wartość reprezentująca niewiadomą 😉

Należy pamiętać o tym, że pusty ciąg znakowy nie jest równoznaczny wartości NULL! Pusty ciąg znakowy jest konkretną wartością – nie jest niewiadomą.

Operatory dla NULL

NULL którego omawiam w tym artykule posiada także swoje operatory. Są nimi IS NULL i IS NOT NULL. Standardowo można je zastosować w klauzuli WHERE przy filtrowaniu wierszy.

select 
cust_id,cust_first_name, cust_last_name, cust_marital_status 
from sh.customers
where cust_marital_status is null
order by 1;
CUST_IDCUST_FIRST_NAMECUST_LAST_NAMECUST_MARITAL_STATUS
1AbigailKessel
2AnneKoch
3BuickEmmerson
4FrankHardy
select 
cust_id,cust_first_name, cust_last_name, cust_marital_status 
from sh.customers
where cust_marital_status is not null
order by 1;
CUST_IDCUST_FIRST_NAMECUST_LAST_NAMECUST_MARITAL_STATUS
6HarriettCharlessingle
7LinetteIngramsingle
8VidaPuleomarried
9RosamondKriderdivorced

Jak widzisz zapytanie pierwsze zwróciło tylko rekordy z NULL. Drugie z kolei zwraca tylko te rekordy w których podana jest konkretna wartość w kolumnie CUST_MARITAL_STATUS.

Three-Valued Logic

Standardowa logika boolowska przewiduje dwie wartości – true i false. W przypadku sytuacji gdzie w logice występuje także wartość niewiadoma, czyli nasz NULL, mówimy już o logice trzech wartości(Three-Valued Logic). Zastanawiasz się dlaczego wchodzimy tutaj w tematy matematyczne i co to ma wspólnego z SQL?

Już odpowiadam, pamiętasz zapewne operatory które przerabialiśmy. Porównywanie wartości przy pomocy znaków <,> i = i logiki AND/OR Wyobraź sobie teraz, porównujesz w klauzuli WHERE wartość liczbową 100, a po drugiej stronie masz… NULL! Jaki będzie wynik takiego porównania?

Rzućmy okiem na przyjazne tabelki, a następnie wróćmy do naszego SQL 😉

WartośćNOT
TrueFalse
FalseTrue
NULLNULL
ANDTrueFalseNULL
TrueTrueFalseNULL
FalseFalseFalseFalse
NULLNULLFalseNULL
ORTrueFalseNULL
TrueTrueTrueTrue
FalseTrueFalseNULL
NULLTrueNULLNULL

Praktyczne zastosowanie Three-Valued Logic

Proste porównanie

select employee_id, first_name, last_name, salary, commission_pct
from hr.employees;
EMPLOYEE_IDFIRST_NAMELAST_NAMESALARYCOMMISSION_PCT
100StevenKing24000
101NeenaKochhar17000
102LexDe Haan17000
103AlexanderHunold9000

Zapytanie które widzisz wyżej pobiera dane z tabeli EMPLOYEES z schematu HR. Tabelka ta pokazuje pracowników firmy, ich zarobki, a także procentową premie którą otrzymują. Nie każdy pracownik dostaje premie, dlatego w kolumnie COMMISSION_PCT pojawia się wartość NULL. Przejrzyj wynik tego zapytania dokładnie, skup się na tym jak duże SALARY otrzymują pracownicy 😉

select employee_id, first_name, last_name, salary, commission_pct
from hr.employees
where salary + commission_pct*salary > 1200;
EMPLOYEE_IDFIRST_NAMELAST_NAMESALARYCOMMISSION_PCT
145JohnRussell14000.4
146KarenPartners13500.3
147AlbertoErrazuriz12000.3
148GeraldCambrault11000.3

Zapytanie które przed chwilą zobaczyłeś filtruje pracowników. Chcemy wyświetlić tylko tych, których pensja z premią jest wyższa niż 1200. Gdy dokładnie przejrzysz wynik tego zapytania, zauważysz że lista wyświetlonych osób jest dosyć krótka. Co więcej powinieneś zauważyć ciekawą rzecz, zostali pominięci wszyscy Ci których SALARY jest zdecydowanie powyżej 1200, ale nie mają przydzielonej żadnej premii!

Sytuacja wyżej opisana jest typowym przykładem logiki trzech wartości. Pracownicy którzy mają zarobki powyżej 1200, ale nie mający przydzielonej premii, nie zostali wyświetleni w wynikach zapytania. W ich przypadku jednym z elementów porównania „salary + commission_pct*salary > 1200” była wartość NULL, która spowodowała że wynik porównania stał się też NULL. Klauzula WHERE zwraca tylko te wiersze dla których wynik operacji logicznej to true 😉


Zostań Administratorem Baz Danych! 

Przejdź kompleksowy kurs: Praktyczna Administracja Oracle Database!


Operator AND

Poniższe zapytanie wykorzystuje bardziej złożony warunek z operatorem AND. W przypadku wystąpienia wartości niewiadomej w którymś z elementów operacji logicznej, wynik również jest niewiadomą.

select employee_id, first_name, last_name, salary, commission_pct
from hr.employees
where salary > 1200 and commission_pct > 0.2;
EMPLOYEE_IDFIRST_NAMELAST_NAMESALARYCOMMISSION_PCT
145JohnRussell14000.4
146KarenPartners13500.3
147AlbertoErrazuriz12000.3
148GeraldCambrault11000.3

Tak jak w poprzednim przykładzie, w wyniku zapytania nie pojawiają się pracownicy bez przydzielonej premii.

Operator OR

Tym razem zmodyfikowałem zapytanie i wstawiłem do niego operator OR.

select employee_id, first_name, last_name, salary, commission_pct
from hr.employees
where salary > 1200 or commission_pct > 0.2;
EMPLOYEE_IDFIRST_NAMELAST_NAMESALARYCOMMISSION_PCT
100StevenKing24000
101NeenaKochhar17000
102LexDe Haan17000
103AlexanderHunold9000

W wynikach będziesz widzieć tym razem także pracowników bez premii. Operator logiczny OR wymaga by tylko jedna z stron zwróciła true, zatem wartość niewiadoma po drugiej stronie nie ma znaczenia, jeżeli tylko pierwsza ze stron zwraca true.

Funkcje NVL() i NVL2()

Musimy zatem poradzić sobie z problemem który pojawił się w poprzednich przykładach. Baza danych Oracle ma do tego celu przygotowane dwie funkcje.

Funkcja NVL()

Pierwsza z wspomnianych funkcji to NVL(). Ma ona dwa parametry, pierwszy z nich przyjmuje wartość która jest sprawdzana pod kątem bycia NULL. Drugi z parametrów to wartość która ma być wstawiona, jeżeli się okaże że wynik sprawdzenia zwrócił NULL.

select employee_id, first_name, last_name, salary, NVL(commission_pct, 0)
from hr.employees; 
EMPLOYEE_IDFIRST_NAMELAST_NAMESALARYNVL(COMMISSION_PCT,0)
100StevenKing240000
101NeenaKochhar170000
102LexDe Haan170000
103AlexanderHunold90000

Jak widzisz wszędzie tam gdzie wartość była niewiadomą, mamy teraz konkretną wartość, w tym wypadku 0. W wierszach gdzie była podana wartość premii, dane pokazywane są bez zmian.

Funkcja NVL2()

Funkcja NVL2() różni się od poprzedniej dodatkowym, trzecim parametrem. Możemy użyć go w celu podmieniana dodatkowo wartości dla których nie wyszedł NULL podczas sprawdzenia. Najlepiej zobacz to na przykładzie 😉

select employee_id, first_name, last_name, salary, NVL2(commission_pct, 0,0.1)
from hr.employees; 
EMPLOYEE_IDFIRST_NAMELAST_NAMESALARYNVL2(commission_pct, 0,0.1)
143RandallMatos2600.1
144PeterVargas2500.1
145JohnRussell140000
146KarenPartners135000

Funkcja NVL2() podmieniła nieznane wartości na 0, natomiast wartości inne znane na 0.1 😉

Zadanie domowe!

Naszym celem jest wyświetlenie listy pracowników, którzy zarabiają więcej niż 1200, niezależnie od tego czy dostają premie czy nie. Jak zatem należy poprawić poniższe zapytanie, aby pozyskać właściwy zestaw danych?

select employee_id, first_name, last_name, salary, commission_pct
from hr.employees
where salary + commission_pct*salary > 1200;

✅ Napisz w komentarzu poniżej jaka jest poprawna odpowiedź!✅ 

W kolejnej części Darmowego Kursu SQL pokażę Ci jak pobierać dane z wielu tabel jednocześnie.


Szósta część kursu już dostępna! Kurs SQL #06: Łączenie tabel