2011-12-21 17 views
9

Ho una vaga, forse la memoria cargo-cult da anni di lavoro con SQL Server che quando hai una colonna eventualmente nullo, non è sicuro di scrivere "WHERE" clausola predicati come:SQL e logici operatori e controlli nulli

... WHERE the_column IS NULL OR the_column < 10 ... 

aveva qualcosa a che fare con il fatto che le regole SQL non prevedono cortocircuito (e in effetti questo è sorta-di una cattiva idea forse per motivi di ottimizzazione query), e quindi la " < "il confronto (o qualsiasi altra cosa) potrebbe essere valutato anche se il valore della colonna è nullo. Ora, esattamente il motivo per cui che sarebbe una cosa terribile, non lo so, ma mi ricordo di essere severamente avvertiti da alcuni documenti per sempre il codice che come un "caso" clausola:

... WHERE 1 = CASE WHEN the_column IS NULL THEN 1 WHEN the_column < 10 THEN 1 ELSE 0 END ... 

(il goofy "1 =" parte è perché SQL Server non/non ha avuto booleani di prima classe, o almeno ho pensato che non ha fatto)

Quindi le mie domande sono qui:.

  1. Is questo è vero per SQL Server (o forse per Back-Rev SQL Server 2000 o 2005) o sono semplicemente pazzo?
  2. In caso affermativo, la stessa avvertenza si applica a PostgreSQL? (8.4 se è importante)
  3. Qual è esattamente il problema? Ha a che fare con il modo in cui gli indici funzionano o qualcosa del genere?

La mia messa a terra in SQL è piuttosto debole.

+1

Forse stavano parlando di AND? Dal momento che null AND qualsiasi cosa è nullo, è spesso necessaria la coalesce o un caso in cui le espressioni possono contenere termini nulli. –

risposta

10

Non so SQL Server, quindi non posso parlare che.

Data un'espressione a L b per qualche operatore logico L, non v'è alcuna garanzia che a saranno valutate prima o dopo b o addirittura che sia a e b saranno valutate:

Expression Evaluation Rules

L' l'ordine di valutazione delle sottoespressioni non è definito. In particolare, gli input di un operatore o di una funzione non sono necessariamente valutati da sinistra a destra o in qualsiasi altro ordine fisso.

Inoltre, se il risultato di un'espressione può essere determinata valutando solo alcune parti di esso, poi altre sottoespressioni potrebbero non essere valutati affatto.
[...]
Si noti che questo non è lo stesso del "cortocircuito" da sinistra a destra degli operatori booleani che si trova in alcuni linguaggi di programmazione.

Di conseguenza, non è saggio utilizzare le funzioni con effetti collaterali come parte di espressioni complesse. È particolarmente pericoloso fare affidamento sugli effetti collaterali o sull'ordine di valutazione nelle clausole WHERE e HAVING, poiché tali clausole sono ampiamente rielaborate come parte dello sviluppo di un piano di esecuzione.

Per quanto espressione della forma:

the_column IS NULL OR the_column < 10 

è interessato, non c'è nulla di cui preoccuparsi in quanto NULL < n è NULL per tutti n, anche NULL < NULL viene valutato come NULL; inoltre, NULL non vale così

null is null or null < 10 

è solo un modo complicato di dire true or null e questo è true indipendentemente dal sub-espressione viene valutata prima.

L'intero "use a CASE" suona per me quasi come SQL carico-cult. Tuttavia, come la maggior parte del culto del cargo, c'è un nucleo nascosto sotto il carico; appena sotto il mio primo estratto dal manuale di PostgreSQL, troverete questo:

Quando è essenziale per forzare ordine di valutazione, un CASE costrutto (vedi Sezione 9.16) può essere utilizzato. Ad esempio, questo è un modo inaffidabile di cercare di evitare la divisione per zero in una clausola WHERE:

SELECT ... WHERE x > 0 AND y/x > 1.5; 

Ma questo è sicuro:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END; 

Quindi, se avete bisogno di guardia contro un condizione che solleverà un'eccezione o avere altri effetti collaterali, quindi è necessario utilizzare un CASE per controllare l'ordine di valutazione come CASE è evaluated in order:

Ogni condizione è un'espressione che restituisce un risultato boolean. Se il risultato della condizione è true, il valore dell'espressione CASE è che segue la condizione e il resto dell'espressione CASE non viene elaborato. Se il risultato della condizione non è vero, tutte le successive clausole WHEN vengono esaminate nello stesso modo.

Quindi, dato queste:

case when A then Ra 
    when B then Rb 
    when C then Rc 
    ... 

A è garantito da valutare prima di B, B prima C, ecc e la valutazione si ferma non appena una delle condizioni restituisce un valore vero.

In sintesi, un CASE cortocircuiti senza ma né ANDOR corto circuito in modo che solo bisogno di utilizzare un CASE quando è necessario per la protezione contro gli effetti collaterali.

+1

Sì, grazie; Capisco il fatto che SQL non impone una regola di cortocircuito (o, in alternativa, un "non cortocircuito"). La domanda riguarda davvero se qualcosa di terribile accade se un confronto relazionale ordinario viene valutato rispetto a una colonna forse-nulla. Grazie per la risposta molto dettagliata. – Pointy

1

Non ho mai sentito parlare di un problema simile, e this bit of SQL Server 2000 documentation utilizza WHERE advance < $5000 OR advance IS NULL in un esempio, quindi non deve essere stata una regola molto severa. La mia unica preoccupazione con OR è che ha una precedenza inferiore a AND, quindi potresti scrivere accidentalmente qualcosa come WHERE the_column IS NULL OR the_column < 10 AND the_other_column > 20 quando non è quello che intendi; ma la solita soluzione è parentesi piuttosto che una grande espressione CASE.

Penso che nella maggior parte degli RDBMS, gli indici non includano valori nulli, quindi un indice su the_column non sarebbe terribilmente utile per questa query; ma anche se così non fosse, non vedo perché una grande espressione CASE sarebbe più adatta all'indice.

(Naturalmente, è difficile fornire una prova negativa, e forse qualcun altro sa che cosa ti riferisci?)

1

Bene, ho ripetutamente scritto query come il primo esempio da sempre (diamine, ho scritto generatori di query che generano query del genere), e non ho mai avuto un problema.

penso che si può essere un po 'ricordando ammonimento qualcuno ti ha dato qualche volta contro la scrittura funky unirsi condizioni che utilizzano OR. Nel tuo primo esempio, le condizioni unite dallo OR limitano la stessa colonna della stessa tabella, che è OK. Se la tua seconda condizione era una condizione di join (cioè, vincolava colonne da due tabelle diverse), allora potresti entrare in situazioni brutte in cui il pianificatore di query non ha altra scelta che usare un join cartesiano (cattivo, cattivo, cattivo !!!).

Non penso che la tua funzione CASE stia facendo davvero qualcosa lì, tranne forse ostacolare i tentativi del tuo pianificatore di query di trovare un buon piano di esecuzione per la query.

Ma più in generale, è sufficiente scrivere prima la query semplice e vedere come funziona per dati realistici. Non c'è bisogno di preoccuparsi di un problema che potrebbe non esistere nemmeno!

0

I valori Null possono essere confusi. Il "... WHERE 1 = CASE ..." è utile se si sta tentando di passare un valore NULL o un valore come parametro ex. "DOVE the_column = @parameter. Questo post può essere utile Passing Null using OLEDB.

1

Invece di

the_column IS NULL OR the_column < 10 

farei

isnull(the_column,0) < 10 

o per il primo esempio

WHERE 1 = CASE WHEN isnull(the_column,0) < 10 THEN 1 ELSE 0 END ... 
0

Un altro esempio in cui CASE è utile è quando si usano le funzioni di data sulle colonne varchar aggiungendo ISDATE prima di u canta say convert (colA, datetime) potrebbe non funzionare, e quando colA ha dati non datati la query può dare errori.

Problemi correlati