Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Wybór NOT IN z wartościami NULL

Najpierw trochę teorii:Null (SQL)

Najważniejsze dla nas części z powyższego linku:

Porównania z NULL i logiką trójwartościową (3VL)

Ponieważ Null nie należy do żadnej domeny danych, nie jest uważany za „wartość”, ale raczej za znacznik (lub symbol zastępczy) wskazujący na brak wartości. Z tego powodu porównania z wartością Null nigdy nie mogą skutkować ani prawdą, ani fałszem, ale zawsze trzecim logicznym wynikiem, nieznanym.[8] Logiczny wynik poniższego wyrażenia, które porównuje wartość 10 do Null, jest nieznany:

SELECT 10 = NULL       -- Results in Unknown

aby oba porównania:x = NULL i x <> NULL ocenia na NULL(nieznane).

SQL implementuje trzy logiczne wyniki, więc implementacje SQL muszą zapewniać wyspecjalizowaną logikę trójwartościową (3VL). Reguły rządzące trójwartościową logiką SQL są pokazane w poniższych tabelach (p i q reprezentują stany logiczne)”[9] Tabele prawdy używane przez SQL dla AND, OR i NOT odpowiadają wspólnemu fragmentowi trójwartościowej logiki Kleene i Łukasiewicza ( które różnią się definicją implikacji, jednak SQL nie definiuje takiej operacji).

+---------+-------------+-------------+-------------+-----------+--------+
|    p    |        q    |     p OR q  |     p AND q |    p = q  |p != q  |
+---------+-------------+-------------+-------------+-----------+--------+
| True    |     True    |     True    |     True    |   True    | False  |
| True    |     False   |     True    |     False   |   False   | True   |
| True    |     Unknown |     True    |     Unknown |   Unknown | Unknown|
| False   |     True    |     True    |     False   |   False   | True   |
| False   |     False   |     False   |     False   |   True    | False  |
| False   |     Unknown |     Unknown |     False   |   Unknown | Unknown|
| Unknown |     True    |     True    |     Unknown |   Unknown | Unknown|
| Unknown |     False   |     Unknown |     False   |   Unknown | Unknown|
| Unknown |     Unknown |     Unknown |     Unknown |   Unknown | Unknown|
+---------+-------------+-------------+-------------+-----------+--------+

Efekt nieznany w klauzulach WHERE

Trójwartościowa logika SQL występuje w języku manipulacji danymi (DML) w predykatach porównawczych instrukcji i zapytań DML. Klauzula WHERE powoduje, że instrukcja DML działa tylko na tych wierszach, dla których predykat ma wartość Prawda.

W skrócie:klauzula WHERE traktuje NULL jako FALSE

Rozważ teraz prostszy przypadek:

SELECT * FROM T1;

|      X |
|--------|
|      1 |
| (null) |

i zapytanie:

SELECT * FROM t1 WHERE x IN (1, NULL);

Powyższe zapytanie jest skrótem do tego:

SELECT * FROM t1 
WHERE x = 1
  OR  x = NULL

Dla drugiego wiersza z tabeli t ( x =NULL) ten warunek wygląda następująco:

WHERE NULL = 1
   OR NULL = NULL

więc ten warunek dla wiersza x=NULL ocenia na NULL, ponieważ NULL=1 ma wartość NULL, NULL=NULL ma wartość NULL, a NULL OR NULL jest również NULL (patrz tabela 3VL powyżej).

Rozważmy teraz ciekawszy przypadek:

SELECT * FROM t1 WHERE x NOT IN (1, NULL);

Ta klauzula x NOT IN (1, NULL) jest równoważne NOT ( x IN (1, NULL) )
więc jest to również równoważne:

NOT (
  x = 1
  OR
  x = NULL
)

i zgodnie z prawami De Morgana jest to równoważne:

NOT ( x = 1 ) AND NOT ( x = NULL )

i (jeśli zastąpimy NOT x = y z x <> y ) jest również odpowiednikiem:

 x <> 1 AND x <> NULL

Przyjrzyj się uważnie ostatniemu warunkowi:

WHERE 
x <> 1 AND x <> NULL

Wiemy, że x <> NULL zawsze ma wartość NULL. Wiemy również z powyższej tabeli 3VL, że zarówno true AND NULL jest NULL i false AND NULL zwraca FALSE, więc cały warunek zawsze zwraca FALSE lub NULL, ale nigdy nie zwraca TRUE.

Dlatego zapytanie z tym warunkiem:

SELECT .....
WHERE x NOT IN ( NULL, whatever)

zawsze zwraca pusty zestaw wyników

A teraz twoje zapytanie, które również jest ciekawe:

SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);

które można przepisać (przy użyciu stałych wartości) na:

SELECT * FROM t1
WHERE (id, val) NOT IN (
       (1, null),
       (2, 2 )
)

To zapytanie używa tak zwanego wyrażenia wartości wiersza

Zasadniczo warunek używający wyrażenia wartości wiersza w ten sposób

(a, b) = (x, y)

jest odpowiednikiem tego:

a = x AND b = y

więc powyższe zapytanie można przepisać na to:

SELECT * FROM t1
WHERE NOT (
   id = 1 AND val = NULL
   OR
   id = 2 AND val = 2
)

Zgodnie z prawami De Morgana jest to identyczne z:

SELECT * FROM t1
WHERE 
   NOT ( id = 1 AND val = NULL )
   AND
   NOT ( id = 2 AND val = 2 )

i dalej do:

SELECT * FROM t1
WHERE 
   ( id <> 1 OR val <> NULL )
   AND
   ( id <> 2 OR val <> 2 )

Od pierwszej części ( id <> 1 OR val <> NULL ) warunku ma wartość prawda tylko w przypadku, gdy id <> 1 (patrz tabela 3VL powyżej), ten warunek można uprościć do:

SELECT * FROM t1
WHERE 
   ( id <> 1 )
   AND
   ( id <> 2 OR val <> 2 )

i dalej (zgodnie z prawem De Morgana) na:

SELECT * FROM t1
WHERE 
   id <> 1 AND id <> 2
   OR
   id <> 1 AND  val <> 2

więc ani (1,1) ani (2,2) ze źródła data1 przestrzegać tych warunków.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wykrywanie cykli z rekurencyjnym faktoringiem podzapytań

  2. Wyeliminuj duplikaty za pomocą funkcji Oracle LITAGG

  3. Obsługa błędów Oracle

  4. Odzyskiwanie plików edytora SQL (zapytania, procedury) po awarii lub zawieszeniu się ropuchy w przypadku awarii lub zawieszenia Oracle

  5. Jak uniknąć błędów mutacji tabel