wtorek, 31 grudnia 2013

Wartość NULL w bazach danych

Pracując z bazami danych, każdy z nas spotkał się kiedyś z wartością NULL, która reprezentuje tak zwaną wartość nieznaną. Posiada ona specjalne wymagania przy
  • złączeniach
  • funkcjach agregujących
  • operatorach grupowania

Warto zapoznać się głębiej z tymi cechami bo czasami mogą nas zaskoczyć.

Operator porównywania dla wartości NULL

exmaples=# \pset null 'NULL'
Null display is "NULL".
exmaples=# select 
 1 = NULL AS "1 = NULL", 
 true OR NULL as "true OR NULL", 
 true AND NULL as "true AND NULL", 
 false OR NULL as "false OR NULL", 
 false AND NULL as "false AND NULL", 
 NULL = NULL as "NULL = NULL", 
 NULL IS NULL as "NULL IS NULL";

-[ RECORD 1 ]--+-----
1 = NULL       | NULL
true OR NULL   | t
true AND NULL  | NULL
false OR NULL  | NULL
false AND NULL | f
NULL = NULL    | NULL
NULL IS NULL   | t

  • Wyrażenie A = B mówi, że A "equal to" B. Porównanie wartości A do NULL wraca NULL nawet w przypadku gdy porównujemy dwie wartości NULL (NULL jest wartością NIEZNANĄ!)
  • Operator IS jako argument przyjmuje wartości logiczne. Wartość logiczna z NULL odpowiada wartości nieznanej. Dlatego zwraca zawsze wartość boolean: true lub false

Wyszukiwanie wartości NULL i NOT NULL

Dla celów testowych stworzyłam tabelę o nazwie test_null, która przyjmuje dla niektórych wierszy w kolumnie a i b, wartości NULL. Poniżej zobaczycie podstawowe zapytania wyszukiwania wartości NULL.
exmaples=# select * from test_null;
  a   |  b   | c 
------+------+---
 NULL |    1 | f
    1 |    2 | f
 NULL |    3 | f
    2 |    3 | f
 NULL | NULL | f
    3 |    2 | t
   39 |    3 | t
   39 | NULL | t
   37 |    2 | t
(9 rows)

1. Warunek NULL na kolumnie a
exmaples=# select * from test_null where a is NULL;
  a   |  b   | c 
------+------+---
 NULL |    1 | f
 NULL |    3 | f
 NULL | NULL | f
(3 rows)

2. Warunek NOT NULL na wszystkich wierszach:
exmaples=# select t.* from test_null t where t.* is NOT NULL;
 a  | b | c 
----+---+---
  1 | 2 | f
  2 | 3 | f
  3 | 2 | t
 39 | 3 | t
 37 | 2 | t
(5 rows)

Polecenie SELECT z warunkiem IN i NOT IN

Dla powyższej tabeli chcielibyśmy wyciągnąć wiersze gdzie b jest równe 2 lub jest wartością NULL. Kiedy jednak wykonujemy poniższe zapytanie za pomocą operatora IN, wynik może nas zaskoczyć.
Zmieniam warunek zapytania i chcę jednak dostać wszystkie wiersze, gdzie kolumna b nie przyjmuje wartości 1 i 3. Wynik jest taki sam.
exmaples=# select * from test_null where b IN (2,NULL);
 a  | b | c 
----+---+---
  1 | 2 | f
  3 | 2 | t
 37 | 2 | t
(3 rows)

exmaples=# select * from test_null where b NOT IN (1,3);
 a  | b | c 
----+---+---
  1 | 2 | f
  3 | 2 | t
 37 | 2 | t
(3 rows)
Jeśli jednak w warunku NOT IN pojawi się NULL, nie otrzymujemy już żadnych wierszy:
exmaples=# select * from test_null where b NOT IN (2,NULL);
 a | b | c 
---+---+---
(0 rows)

A IN (B) jest traktowane jako A =ANY B, a A NOT IN B jako NOT(A IN B).

Jednak A=ANY B wraca false jeśli A=b dla wszystkich b, które są w zbiorze B, a ponieważ A=NULL zwraca wartość nieznaną a nie false, to NOT IN nie może zwrócić wartości true.

Aby zabezpieczyć się przed takim scenariuszem, a nie możemy zmienić konstrukcji zapytania, musimy dodać warunek IS NOT NULL w podzapytaniu. (Pamiętajmy o tym gdy w warunku IN podstawiamy subselect)

Funkcje agregujące i grupowanie

Dla tej samem tabeli testowej wyciągniemy podstawowe informację o ilości wierszy i sumie wartości kolumny b:
exmaples=# select count(*), count(b), sum(b) from test_null;
 count | count | sum 
-------+-------+-----
     9 |     7 |  16
(1 row)
Grupowanie
exmaples=# select sum(a), b from test_null group by b having sum(a) < 40;
 sum |  b   
-----+------
  39 | NULL
(1 row)
Wartości NULL nie są brane pod uwagę dla działania funkcji agregujących. Warunek Having działa tak samo jak dla operatory porównywania.

Warto wiedzieć o specyficznych zachowaniach wartości NULL gdy mamy podejrzenie, że zwracany wynik naszego zapytania SELECT może być nieprawidłowy.
Powodzenia :-)

Brak komentarzy:

Prześlij komentarz