Oracle
 sql >> Database >  >> RDS >> Oracle

NOT IN selezione con valori NULL

Prima un po' di teoria:Null (SQL)

Le parti più importanti per noi dal link sopra:

Confronto con NULL e la logica a tre valori (3VL)

Poiché Null non è un membro di alcun dominio di dati, non è considerato un "valore", ma piuttosto un marker (o segnaposto) che indica l'assenza di valore. Per questo motivo, i confronti con Null non possono mai risultare né Vero né Falso, ma sempre in un terzo risultato logico, Sconosciuto.[8] Il risultato logico dell'espressione seguente, che confronta il valore 10 con Null, è Sconosciuto:

SELECT 10 = NULL       -- Results in Unknown

in modo che entrambi i confronti:x = NULL e x <> NULL restituisce NULL(sconosciuto).

SQL implementa tre risultati logici, quindi le implementazioni SQL devono fornire una logica a tre valori specializzata (3VL). Le regole che governano la logica a tre valori di SQL sono mostrate nelle tabelle seguenti (p eq rappresentano gli stati logici)"[9] Le tabelle di verità utilizzate da SQL per AND, OR e NOT corrispondono a un frammento comune della logica a tre valori di Kleene e Łukasiewicz ( che differiscono nella loro definizione di implicazione, tuttavia SQL non definisce tale operazione).

+---------+-------------+-------------+-------------+-----------+--------+
|    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|
+---------+-------------+-------------+-------------+-----------+--------+

Effetto di Sconosciuto nelle clausole WHERE

La logica SQL a tre valori viene rilevata in Data Manipulation Language (DML) nei predicati di confronto di istruzioni e query DML. La clausola WHERE fa sì che l'istruzione DML agisca solo su quelle righe per le quali il predicato restituisce True.

Quindi, in breve:la clausola WHERE considera NULL come FALSO

Consideriamo ora un caso più semplice:

SELECT * FROM T1;

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

e una domanda:

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

La query di cui sopra è una scorciatoia per questa:

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

Per la seconda riga della tabella t ( x =NULL) questa condizione è simile a:

WHERE NULL = 1
   OR NULL = NULL

quindi questa condizione per la riga x=NULL restituisce NULL perché NULL=1 è NULL, NULL=NULL è NULL e NULL OR NULL è anche NULL (consultare la tabella 3VL sopra).

Consideriamo ora un caso più curioso:

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

Questa clausola x NOT IN (1, NULL) è equivalente a NOT ( x IN (1, NULL) )
quindi equivale anche a:

NOT (
  x = 1
  OR
  x = NULL
)

e secondo le leggi di De Morgan equivale a:

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

e (se sostituiamo NOT x = y con x <> y ) è anche equivalente a:

 x <> 1 AND x <> NULL

Guarda attentamente l'ultima condizione:

WHERE 
x <> 1 AND x <> NULL

Sappiamo di x <> NULL restituisce sempre NULL. Sappiamo anche dalla tabella 3VL sopra, che sia true AND NULL è NULL e false AND NULL restituisce FALSE, quindi l'intera condizione restituisce sempre FALSE o NULL, ma non restituisce mai TRUE.

Quindi una query con questa condizione:

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

Restituisce sempre un set di risultati vuoto

E ora la tua domanda, che è anche curiosa:

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

che può essere riscritto (usando valori costanti) in:

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

Questa query utilizza la cosiddetta espressione del valore di riga

Sostanzialmente una condizione che utilizza il valore di riga espresso in questo modo

(a, b) = (x, y)

è equivalente a questo:

a = x AND b = y

quindi la query sopra può essere riscritta in questa:

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

Secondo le leggi di De Morgan questo è identico a:

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

e oltre a:

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

Dalla prima parte ( id <> 1 OR val <> NULL ) della condizione restituisce true solo nel caso in cui id <> 1 (consultare la tabella 3VL sopra), questa condizione può essere semplificata in:

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

e inoltre (secondo le leggi di De Morgan) in:

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

quindi nemmeno (1,1)(2,2) dalla fonte data1 rispettare queste condizioni.