2012-05-02 10 views

risposta

34

La IS DISTINCT FROM predicato è stato introdotto come caratteristica T151 di SQL: 1999, e la sua negazione leggibile, IS NOT DISTINCT FROM, è stata aggiunta come caratteristica T152 di SQL: 2003. Lo scopo di questi predicati è garantire che il risultato del confronto di due valori sia True o False, mai Unknown.

Questi predicati funzionano con qualsiasi tipo comparabile (incluse righe, matrici e multiset) rendendo piuttosto complicato emularli esattamente. Tuttavia, SQL Server non supporta la maggior parte di questi tipi, in modo che possiamo ottenere abbastanza lontano controllando per argomenti nulli/operandi:

  • a IS DISTINCT FROM b può essere riscritta come:

    ((a <> b OR a IS NULL OR b IS NULL) AND NOT (a IS NULL AND b IS NULL)) 
    
  • a IS NOT DISTINCT FROM b può essere riscritta come:

    (NOT (a <> b OR a IS NULL OR b IS NULL) OR (a IS NULL AND b IS NULL)) 
    

La propria risposta è errata in quanto non considera che FALSE OR NULL restituisce sconosciuto. Ad esempio, NULL IS DISTINCT FROM NULL deve essere valutato su False. Allo stesso modo, 1 IS NOT DISTINCT FROM NULL deve essere valutato su False.In entrambi i casi, le tue espressioni producono Unknown.

+0

((a <> b O a IS NULL O b IS NULL) E NON (a IS NULL E b IS NULL)): non possiamo solo <> b o a è nullo xor b è nullo –

12

Se l'implementazione SQL non implementa lo standard IS DISTINCT FROM e IS NOT DISTINCT FROM operatori SQL, è possibile riscrivere espressioni che li contengono utilizzando i seguenti equivalenze:

In generale:

a IS DISTINCT FROM b <==> 
(
    ((a) IS NULL AND (b) IS NOT NULL) 
OR 
    ((a) IS NOT NULL AND (b) IS NULL) 
OR 
    ((a) <> (b)) 
) 

a IS NOT DISTINCT FROM b <==> 
(
    ((a) IS NULL AND (b) IS NULL) 
OR 
    ((a) = (b)) 
) 

Questa risposta non è corretta quando viene utilizzato in un contesto in cui la differenza tra UNKNOWN e FALSE è importante. Penso che sia raro, però. Vedi la risposta accettata da @ChrisBandy.

Se un valore segnaposto può essere identificato che in realtà non si verifica nei dati, quindi COALESCE è un'alternativa:

a IS DISTINCT FROM b <==> COALESCE(a, placeholder) <> COALESCE(b, placeholder) 
a IS NOT DISTINCT FROM b <==> COALESCE(a, placeholder) = COALESCE(b, placeholder) 
+5

Questa è una risposta sbagliata però. Vedi l'ultimo paragrafo nella risposta di Chris. –

+1

Sì, questa risposta non è corretta se utilizzata in un contesto in cui è importante la differenza tra UNKNOWN e FALSE. Penso che sia raro, però. –

+3

@JasonKresowaty: Questo non è affatto raro. In qualsiasi predicato come '(a IS DISTINCT FROM b) AND something', la distinzione tra' UNKNOWN' e 'FALSE' è essenziale. Se 'a' e' b' sono entrambi 'NULL', la tua emulazione genererà' NULL', indipendentemente dal fatto che 'something' sia' TRUE' o 'FALSE'. –

4

Un avvertimento in riscrittura è distinto da e non è distinto dal sarebbe per non interferire con l'utilizzo di indici, almeno quando si utilizza SQL Server. In altre parole, quando si utilizza il seguente:

WHERE COALESCE(@input, x) = COALESCE(column, x) 

SQL Server non sarà in grado di utilizzare qualsiasi indice che include colonna. Quindi, in una clausola WHERE, sarebbe preferibile utilizzare il modulo

WHERE @input = column OR (@input IS NULL AND column IS NULL) 

di approfittare di tutti gli indici per colonna. (Parens utilizzato solo per chiarezza)

+0

+1 per menzionare per come le funzioni uccidono l'uso dell'indice. È come sono finito qui in primo luogo. – Serge

18

Un'altra soluzione che mi piace sfrutta il vero valore booleano a due valori di EXISTS combinato con INTERSECT. Questa soluzione dovrebbe funzionare in SQL Server 2005+.

  • a IS NOT DISTINCT FROM b può essere scritta come:

    EXISTS(SELECT a INTERSECT SELECT b)

Come documentato, INTERSECT considera due valori NULL uguali, quindi se entrambi sono NULL, quindi intersecano risultati in un'unica fila, così ESISTE si rivela vera.

  • a IS DISTINCT FROM b può essere scritta come:

    NOT EXISTS(SELECT a INTERSECT SELECT b)

Questo approccio rappresenta molto più conciso, se si dispone di più colonne nullable è necessario confrontare in due tabelle. Ad esempio, per restituire righe in TableB che hanno valori diversi per Col1, Col2, o Col3 rispetto TableA, quanto segue può essere utilizzato:

SELECT * 
FROM TableA A 
    INNER JOIN TableB B ON A.PK = B.PK 
WHERE NOT EXISTS(
    SELECT A.Col1, A.Col2, A.Col3 
    INTERSECT 
    SELECT B.Col1, B.Col2, B.Col3); 

Paul White spiega questa soluzione in modo più dettagliato: http://sqlblog.com/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx

+3

Questo dovrebbe essere accettato rispondi, poiché riscrive il predicato in un modo che non duplica i riferimenti a 'a' e' b'. Per espressioni non deterministiche 'a' e' b', o espressioni con effetti collaterali (come la registrazione), sarebbe molto utile. Il tuo secondo esempio emula anche '(A.Col1, A.Col2, A.Col3) IS DISTINCT FROM (B.Col1, B.Col2, B.Col3)', che è supportato solo in modo nativo da PostgreSQL (a mia conoscenza). Un predicato molto utile, a volte. –

+0

Ottima risposta, preferisco quella accettata – Jocie

1

Per il riferimento, l'implementazione più canonica (e leggibile) di IS [ NOT ] DISTINCT FROM sarebbe un'espressione ben formattata CASE. Per IS DISTINCT FROM:

CASE WHEN [a] IS  NULL AND [b] IS  NULL THEN FALSE 
    WHEN [a] IS  NULL AND [b] IS NOT NULL THEN TRUE 
    WHEN [a] IS NOT NULL AND [b] IS  NULL THEN TRUE 
    WHEN [a] =    [b]    THEN FALSE 
    ELSE           TRUE 
END 

Ovviamente, altre soluzioni (specificamente John Keller's, utilizzando INTERSECT) sono più concisi.

More details here

0

Queste espressioni possono essere un buon sostituto per la è distinto dalla logica e prestazioni migliori rispetto ai precedenti esempi perché finiscono per essere compilata per server SQL in una singola espressione predicato che si tradurrà in circa. metà dell'operatore costa su un'espressione di filtro. Sono essenzialmente le stesse delle soluzioni fornite da Chris Bandy, tuttavia usano le funzioni nidificate ISNULL e NULLIF per eseguire i confronti sottostanti.

(...ovviamente ISNULL potrebbe essere sostituito con COALESCE, se si preferisce)

  • a IS DISTINCT FROM b può essere riscritta come:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NOT NULL

  • a IS NOT DISTINCT FROM b può essere riscritta come:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NULL

0
a IS NOT DISTINCT FROM b 

può essere riscritta come:

(a IS NOT NULL AND b IS NOT NULL AND a=b) OR (a IS NULL AND b is NULL) 

a IS DISTINCT FROM b 

può essere riscritta come:

NOT (a IS NOT DISTINCT FROM b)