2009-11-18 15 views
18

Per ragioni indipendenti dalla mia volontà, ho bisogno di unire due tabelle e ho bisogno di valori nulli da abbinare. L'opzione migliore che potevo pensare era quello di sputare fuori un UUID e utilizzarlo come il mio valore di confronto, ma sembra bruttoSQL "Join" su valori nulli

SELECT * FROM T1 JOIN T2 ON nvl(T1.SOMECOL,'f44087d5935dccbda23f71f3e9beb491') = 
    nvl(T2.SOMECOL,'f44087d5935dccbda23f71f3e9beb491') 

Come posso fare di meglio? Questo è su Oracle se è importante, e il contesto è un'applicazione in cui un batch di dati caricati dall'utente deve essere confrontato con un batch di dati esistenti per vedere se qualsiasi riga corrisponde. In retrospettiva avremmo dovuto impedire che le colonne di join in entrambi i dataset contenessero valori null, ma non l'abbiamo fatto e ora dobbiamo conviverci.

Modifica: Per essere chiari, non sono solo interessato con valori null. Se le colonne non sono nulle, voglio che corrispondano ai loro valori effettivi.

+2

sono le tabelle uniti sui valori nulli? Avrai un set di risultati piuttosto ampio, se così fosse. Ad esempio, se ci sono 10 valori null in T1 e 10 valori null in T2, otterrai 100 righe. Sicuramente è possibile partecipare a un altro campo popolato, e basta selezionare ... Dove T1.SomeCol è nullo e T2.SomeCol è nullo –

+0

Funzioni utente avere un impatto drammatico sulle prestazioni quando viene utilizzato in query di grandi dimensioni, forse si potrebbe migliorarlo utilizzando i campi calcolati persistenti. Non ne sono sicuro, sto solo pensando. –

+0

Cosa c'è di sbagliato nella tua soluzione? Invece di UUID, è possibile utilizzare qualsiasi stringa non presente nelle colonne, ad esempio la stringa "null", ad esempio per un footprint di memoria inferiore. –

risposta

33

Forse questo avrebbe funzionato, ma non ho mai provato:

SELECT * 
FROM T1 JOIN T2 
ON T1.SOMECOL = T2.SOMECOL OR (T1.SOMECOL IS NULL AND T2.SOMECOL IS NULL) 
+1

Non ho ancora provato questo, ma il mio cervello continua a insistere che avresti bisogno di un CROSS JOIN invece di un JOIN? Non mi fiderei, però, è stato dover leggere tutto il giorno attraverso il brutto codice Java ed è bruciato. – Powerlord

+1

@R. Bemrose - Dovrebbe avere solo un problema di tipo cross join se hai più null. Se c'è solo un null, dovrebbe funzionare ok. Ma poi di nuovo, se hai più null, non c'è davvero nessuna soluzione dato che sarebbe impossibile decidere quale null abbinare a quale altro null. –

+1

Sono rimasto sorpreso, ma in effetti ha funzionato. – Dan

5

In SQL Server ho usato:

WHERE (a.col = b.col OR COALESCE(a.col, b.col) IS NULL) 

Ovviamente non è efficiente, a causa della O, ma a meno che c'è un valore riservato su cui è possibile mappare NULL su entrambi i lati senza ambiguità o piegatura che è il migliore che si possa fare (e se c'era, perché NULL era permesso anche nel tuo design ...)

+0

Questo è buono, ma isnull è più veloce quando si confrontano solo due valori. – JSideris

+0

Oh ma immagino che sia solo in tsql ... – JSideris

3

Non puoi fare qualunque meglio, ma il JOIN che hai non farà un vero "JOIN" in alcun modo (non ci sarà alcuna correlazione tra T1.SOMECOL e T2.SOMECOL a parte che entrambi hanno un valore NULL per quella colonna). Fondamentalmente ciò significa che non sarai in grado di utilizzare un JOIN su NULL per vedere se le righe corrispondono.

NULL non è mai uguale a un altro NULL. Come può qualcosa di valore sconosciuto essere uguale a qualcos'altro di valore sconosciuto?

+0

So che concettualmente non può. Questa particolare caratteristica dell'applicazione è stata progettata stupidamente. Fondamentalmente, richiediamo che il valore di questa colonna sia univoco nel set di dati. Ciò include il permettere che ci sia una riga (ma solo una) con un valore nullo. Se i nuovi dati arrivano e hanno anche un valore nullo per quella colonna, vogliamo che corrisponda. – Dan

+0

Non concettualmente. In Attualità. Se i dati esistenti hanno una colonna chiave nullo e i nuovi dati hanno una colonna chiave nullo, potrebbero + o non + corrispondere. IOW, data una riga esistente con un valore di chiave NULL e 100 nuove righe ciascuna con un valore di chiave NULL, che corrisponde a una sola riga esistente? Non esiste un "valore NULL univoco" nel senso che i dati stiano cercando di usarlo. O, più esattamente, * tutti * NULL sono unici; in realtà, nessun NULL corrisponde a un altro. –

0

Basta lanciare questo fuori là - c'è un modo in cui si possono unire quei valori nulli in un valore noto, come una stringa vuota? Non sapere molto su come è strutturato il tuo tavolo significa che non posso essere sicuro se perderai il significato in quel modo - cioè avere una stringa vuota rappresenta "l'utente ha rifiutato di inserire un numero di telefono" e NULL è "abbiamo dimenticato chiedergli "o qualcosa del genere?

Le probabilità sono che non è possibile, ne sono sicuro, ma se lo è, avrai valori noti da confrontare e puoi ottenere un join legittimo in questo modo.

0

Non è lo stesso che verificare la presenza di valori null in entrambe le colonne?

SELECT * FROM T1, T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL 

o

SELECT * FROM T1 CROSS JOIN T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL 
+1

sql confronta NULL con la parola chiave "IS", non "==". Infatti, non usa "==" ovunque, semplicemente "=". –

+0

Hai ragione. Modificato. Ma il punto è ancora valido. Non è quello che l'OP ha chiesto è effettivamente il controllo dei valori nulli in entrambe le colonne? Se è necessario unirsi anche agli altri valori, è possibile farlo (T1.SOMECOL È NULL E T2.SOMECOL È NULL) O (T1.SOMECOL = T2.SOMECOL). – Gambler

+0

Non trovo alcun motivo per cui questa risposta abbia -1 dopo essere stata modificata, quindi +1. –

1

Vuoi davvero essere in grado di unire le tabelle se un valore è nullo? Non puoi semplicemente escludere i possibili valori nulli nel predicato di join? Trovo difficile ingannare le righe in due tabelle che possono essere correlate da un valore nullo. Se hai 100 null in table1.col_a e 100 null in table2.col_b, avrai 10000 righe restituite solo per le righe con null. Sembra sbagliato.

Tuttavia, hai detto che ne hai bisogno. Posso suggerire di combinare la colonna nulla in una stringa più piccola poiché i confronti tra i caratteri sono relativamente costosi. Ancora meglio, unire i valori nulli in un numero intero se i dati nelle colonne diventeranno testo. Quindi hai dei "confronti" molto rapidi ed è improbabile che si scontrino con i dati esistenti.

+0

Sono in ritardo per questa festa, ma ho trovato una ragione valida: unendo 'user_segments' a' user_objects'. Se la tabella è partizionata, * devi * anche unire 'partition_name' a' subobject_name'. Tuttavia, su tabelle non partizionate questi valori sono nulli. –

3

Per questo tipo di operazione Oracle utilizza internamente una funzione non documentata sys_op_map_nonnull(), in cui la query sarebbe diventato:

SELECT * 
FROM T1 JOIN T2 ON sys_op_map_nonnull(T1.SOMECOL) = sys_op_map_nonnull(T2.SOMECOL) 

privi di documenti, quindi fate attenzione se seguire questa strada.

3

semplice, utilizzare COALESCE, che restituirà il suo primo parametro non nullo:

SELECT * FROM T1 JOIN T2 ON 
    COALESCE(T1.Field, 'magic string') = 
    COALESCE(T2.Field, 'magic string') 

L'unica cosa che dovrete preoccupare è che 'stringa magica' non può essere tra i valori di legge per il join campo in entrambe le tabelle.

+0

Qual è la differenza tra 'COALESCE' e' NVL' in questo caso? – Dan

+0

Chiedo scusa - Stavo rispondendo a una domanda su MySQL e molto probabilmente l'ho postata nel posto sbagliato dopo aver cercato se prima avessi avuto una risposta. La tua domanda ha ovviamente più di un anno e ha risposto. Avrei dovuto prestare più attenzione, perché un profilo della query in MySQL rivela che 'COALESCE' impedisce l'uso di qualsiasi indice su' Field' - la risposta che hai contrassegnato come corretta non ha questo effetto collaterale. Non è un grosso problema, dato che il join in questo particolare esempio finisce per scansionare entrambe le tabelle. –

-2

È inoltre possibile utilizzare CASE per sostituire il valore nullo in Subquery, quindi unire i risultati:

SELECT T1.COL1 FROM 
(
    (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE1) T1 
    JOIN 
    (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE2) T2 
) 
ON T1.COL1=T2.COL1 
+0

Questo non è diverso dal punto di vista concettuale da quello che l'OP ha dato, e in realtà è peggio visto che X ha più probabilità di essere nel database. Inoltre, le viste in linea non sono necessarie e il JOIN è sbagliato quando hai creato un prodotto cartesiano. –

0

Perché non qualcosa di simile:

SELECT * FROM T1 ENTRA T2 ON NVL (T1. SOMECOL, 'null') = nvl (T2.SOMECOL, 'null')

Non so perché si sta utilizzando l'UUID. È possibile utilizzare qualsiasi stringa non presente nelle colonne, ad esempio la stringa "null", ad esempio, per un minore ingombro di memoria. E la soluzione utilizzando nvl è molto più veloce rispetto alla soluzione con or ... is null proposta da Eric Petroelje, per esempio.

0

Si potrebbe provare a utilizzare con la query di seguito.

SELECT * 
FROM TABLEA TA 
JOIN TABLEB TB ON NVL(TA.COL1,0)=NVL(TB.COL2,0); 
0

Credo che ancora potrebbe potrebbe usare NVL() per unirsi:

SELECT * 
FROM T1 
JOIN T2 ON NVL(T2.COL1,-1)=NVL(T1.COL1,-1); 

, ma sarà necessario aggiungere indici basati funzione su colonne col1

CREATE INDEX IND_1 ON T1 (NVL(COL1,-1)); 
CREATE INDEX IND_2 ON T2 (NVL(COL1,-1)); 

Indici dovrebbe migliorare la velocità del join su NVL (..) in modo significativo.