Perché non posso usare valori null nei join?


13

Ho risolto il problema della query usando ... row_number() over (partition by... questa è una domanda più generale sul perché non possiamo usare colonne con valori null nei join. Perché un null non può essere uguale a un null per il bene di un join?

Risposte:


31

Perché un null non può essere uguale a un null per il bene di un join?

Di 'a Oracle di farlo:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(Si noti che in SQL standard è possibile utilizzare t1.id is not distinct from t2.idper ottenere un operatore di parità null-safe, ma Oracle non lo supporta)

Questo funzionerà solo se il valore di sostituzione (-1 nell'esempio sopra) non viene effettivamente visualizzato nella tabella. Trovare un valore "magico" per i numeri potrebbe essere possibile, ma sarà molto difficile per i valori dei caratteri (soprattutto perché Oracle tratta anche una stringa vuota null)

Inoltre: non idverrà utilizzato alcun indice sulle colonne (è possibile definire un indice basato sulle funzioni con l' coalesce()espressione).

Un'altra opzione che funziona per tutti i tipi, senza valori magici:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

Ma la vera domanda è: ha senso?

Considera i seguenti dati di esempio:

Tabella uno

id
----
1
2
(null)
(null)

Tabella due

id
----
1
2
(null)
(null)
(null)

Quale delle combinazioni di valori null deve essere scelta nel join? Il mio esempio sopra si tradurrà in qualcosa come un cross join per tutti i valori null.

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)

6

In alternativa è possibile far corrispondere due null tra loro usando INTERSECTcome operatore di uguaglianza:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

Vedi questa demo di DBFiddle per un'illustrazione.

Certo, questo sembra piuttosto un boccone, anche se in realtà non è molto più lungo del suggerimento di BriteSponge . Tuttavia, di certo non è una corrispondenza, se perdonate il gioco di parole, alla concisione di quanto detto precedentemente nei commenti in modo standard, che è l' IS NOT DISTINCT FROMoperatore, non ancora supportato in Oracle.


2

Solo per completezza, menzionerò che la funzione SYS_OP_MAP_NONNULLora può essere tranquillamente utilizzata per confrontare valori nulli come è ora documentato nella documentazione 12c. Ciò significa che Oracle non lo rimuoverà semplicemente in modo casuale e romperà il codice.

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

Il vantaggio è che non ti imbatti nel problema dei numeri "magici".

Il riferimento nei documenti Oracle è nelle viste materializzate di base - Scelta degli indici per le viste materializzate .


Quindi è documentato ora? Perché AskTom (nel 2003) ha dichiarato: " - non è documentato, e quindi rischia di andare via o cambiare funzionalità che è abbastanza detto che dovrebbe far andare le persone semplicemente" smettere di leggere "lì e potresti essere davvero arrabbiato nella prossima versione. l'unico modo CORRETTO è: punto where (a = b or (a is null and b is null)) . questo è il mio pensiero al riguardo. Non prenderei in considerazione l'uso sys_op_map_nonnull, ignoro quell'uomo dietro il sipario. "
ypercubeᵀᴹ

Se hai un link, per favore aggiungilo alla domanda. Non ho trovato menzione in Funzioni 12c ma cercare documentazione Oracle e versione specifica è piuttosto difficile.
ypercubeᵀᴹ

2

Puoi unire i valori null usando la decodifica:

on decode(t1.id, t2.id, 1, 0) = 1

decodeconsidera i null uguali, quindi funziona senza numeri "magici". Le due colonne devono avere lo stesso tipo di dati.

Non renderà il codice più leggibile, ma probabilmente comunque migliore di t1.id = t2.id or (t1.id is null and t2.id is null)


1

Perché non puoi usare valori nulli nei join? In Oracle, entrambi i seguenti non valutano true:

  • NULL = NULL
  • NULL <> NULL

Ecco perché dobbiamo IS NULL/ IS NOT NULLcontrollare i valori null.
Per testarlo, puoi semplicemente fare:

SELECT * FROM table_name WHERE NULL = NULL

I join stanno valutando una condizione booleana e non li hanno programmati per operare in modo diverso. È possibile inserire un segno maggiore di nella condizione di join e aggiungere altre condizioni; lo valuta semplicemente come un'espressione booleana.

Immagino che un null non possa essere uguale a un null in join per motivi di coerenza. Sfiderebbe il solito comportamento dell'operatore di confronto.


NULL = anythingrisulta NULLperché lo dice lo standard SQL. Una riga soddisfa la condizione di join solo se l'espressione è vera.
Laurenz Albe,

1
Oltre al dettaglio letterale dell'implementazione (che non è sempre il caso: alcuni DB hanno la possibilità di equiparare NULL a NULL per alcuni / tutti gli scopi) c'è un motivo logico: NULL è sconosciuto. Quando confronti NULL con NULL ti stai chiedendo "questa cosa sconosciuta è uguale a questa altra cosa sconosciuta" a cui l'unica risposta ragionevole è "sconosciuta" - un altro NULL (che è mappato su falso in una situazione di confronto).
David Spillett,

-4

Un valore nullo nella maggior parte dei database relazionali è considerato SCONOSCIUTO. Non deve essere confuso con tutti gli zeri HEX. se qualcosa contiene null (sconosciuto), non è possibile confrontarlo.

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

Il che significa che ogni volta che hai un null come operando in un'espressione booleana, la parte else sarà sempre vera.

Contrariamente all'odio generale nei confronti del null da parte degli sviluppatori, il null ha il suo posto. Se qualcosa è sconosciuto, utilizzare null.


6
In realtà tutti i confronti di esempio che hai, UNKNOWNFALSE
cedi

Hai ragione, tuttavia lo scopo di un'espressione booleana è quello di risultare solo vero o falso, quindi non impazzire qui :).
jujiro,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.