2013-04-10 18 views
13

Ho una query di aggiornamento che aggiorna un campo in una tabella quando il valore non corrisponde a un campo in un'altra tabella.Problemi con confronto SQL e valori nulli

UPDATE table1 
SET  a.field1 = b.field3 
FROM table1 a , 
     table2 b 
WHERE a.field2 = b.field2 
     AND a.field1 <> b.field3 

Il problema che sto avendo è che non è in ripresa quando a.field1 è nullo e b.field3 è un valore o se a.field1 è un valore e b.field3 è nullo.

ho ottenuto intorno a questo con l'aggiunta del seguente ...

UPDATE table1 
SET  a.field1 = b.field3 
FROM table1 a , 
     table2 b 
WHERE a.field2 = b.field2 
     AND (a.field1 <> b.field3 
       OR (a.field1 IS NOT NULL 
       AND b.field3 IS NULL) 
       OR (a.field1 IS NULL 
       AND b.field3 IS NOT NULL) 
      ) 

La mia domanda è più incentrata su perché questo sta accadendo e come migliore struttura della query al fine di impedire questo?

+5

Null è uguale a niente, si tratta di un valore indefinito, che non si può paragonare a nulla (anche se si utilizza '<>'). Ecco perché i record del valore nullo sono omessi. Quindi devi usare 'IS NULL' o' IS NOT NULL' in modo esplicito. –

+0

possibile duplicato di [Non uguale <>! = Operatore in T-SQL su NULL] (http://stackoverflow.com/questions/5658457/not-equal-operator-in-t-sql-on-null) –

+0

I incontrato di recente il problema "opposto". Qualcuno mi ha fornito un'istruzione SQL con un confronto come 'WHERE val = null ...' e * ha effettivamente funzionato * sul nostro sql-server in modo uguale a 'WHERE val IS NULL'! È risultato che c'è un'impostazione 'SET ANSI_NULLS OFF' che è stata attivata sul nostro server. Questa impostazione (deprecata !!) consente confronti nulli, vedere [qui] (https://msdn.microsoft.com/en-gb/en-en/library/ms188048.aspx). – cars10m

risposta

1

Oltre a gestire correttamente la logica NULL, è necessario racchiudere più condizioni da applicare tra parentesi.

Qualcosa come questo (non sono sicuro di aver capito esattamente le tue condizioni).

UPDATE table1 
SET  a.field1 = b.field3 
FROM table1 a , 
     table2 b 
WHERE a.field2 = b.field2 
     AND (
       (a.field1 <> b.field3) 
       OR (a.field1 IS NOT NULL AND b.field3 IS NULL) 
       OR (a.field1 IS NULL AND b.field3 IS NOT NULL) 
      ) 
+0

Grazie ... Ho omesso i paren nell'esempio, ma sono nel mio codice ... Ho modificato per essere più chiaro – Heather

1

Tim Shmelter è proprio nel suo commento, NULL non è uguale a nulla- anche compreso NULL. NULL significa letteralmente che il valore è sconosciuto.

Ciò significa, anche se a.field1 e b.field3 entrambi sono NULL, le condizioni a.field1 <> b.field3 nonché a.field1 = b.field3 sia restituirà sempre false. Provalo e vedrai!

Penso che la soluzione qui non sia nella funzione IFNULL di SQL Server. Si trova più nella tua logica di unione. Hai già la soluzione, ovvero la seconda query nella tua domanda. Quello che consiglierò è che stai giocando un po 'di più con i valori NULL in modo da poter capire cosa sono veramente.

+0

Quindi, da quello che sto leggendo, se quasi non ha bisogno di essere in una query di confronto? – Heather

+2

@Rachcha - Piccola correzione: se uno (o entrambi) a.field1 o b.field3 sono NULL, le condizioni a.field1 <> b.field3 e a.field1 = b.field3 non restituiranno FALSE, ma piuttosto NULL. Nella maggior parte dei casi si comporta come FALSE (come questo) perché non è VERO, ma non è ancora FALSE e quindi non può essere negato a VERO. Se metti NOT in front (a.field1 = b.field3) sarà ancora NULL e si comporterà come FALSE. –

+0

Questo è stato un commento molto utile. Sembra che tu abbia giocato con un sacco di null! – Rachcha

5

Il problema è con il confronto NULL. Se a.field1 o b.field3 è NULL, è necessario utilizzare un'istruzione IS NULL o IS NOT NULL. È possibile utilizzare un valore predefinito per a.field1 e b.field3 con la funzione ISNULL.

ISNULL(a.field1,0) <> ISNULL(b.field3,0) 

in questo caso v'è un confronto con il valore 0.

+0

Non l'ho mai usato ... non lo proverò ... lo proverò ... grazie per il nuovo suggerimento – Heather

+1

@Heather: [la funzione ISNULL è progettata per la presentazione] (http://blogs.msdn.com/b/conor_cunningham_msft/ archivio/2010/12/18/conor-vs-more-sargable-predicates.aspx) (SELECT) e non per il filtro (WHERE). –

+0

@BogdanSahlean Perché? – Norberto108

0

Quando si scrive nella query a.field1 = b.field3 effettivamente fare due ipotesi: campo1 nella tabella A deve contenere un valore e field3 nella tabella B deve contenere anche un valore. Non è possibile confrontare un 'informazione mancante e un'informazione inapplicabile' con un valore. Il risultato di questo confronto è sconosciuto. Puoi dare un'occhiata per ulteriori informazioni su Wikipedia.

+0

mmm non ha visto le risposte in arrivo più veloci delle mie :) – KookieMonster

4

Il risultato del confronto di qualsiasi cosa con NULL, anche se stesso, è sempre NULL (non VERO o FALSO). Utilizzare l'opzione con gli operatori EXISTS ed EXCEPT.

UPDATE table1 
SET a.field1 = b.field3 
FROM table1 a JOIN table2 b ON a.field2 = b.field2 
WHERE EXISTS (
       SELECT a.field1 
       EXCEPT 
       SELECT b.field3 
      ) 
+0

La migliore risposta, come documentato [qui] (http://sqlblog.com/blogs/paul_white/archive/2011/06/21/undocumented- query-piani-uguaglianza-comparisons.aspx). L'opzione 'ISNULL()' funziona alla grande, finché non lo fa. – AHiggins

0

Questo controlla se la Colonna1 e Column2 è uguale, inoltre utilizzato Conversione per VARBINARY per confrontare in maiuscole e minuscole ed è possibile rimuoverlo se non è necessario.

--c1 = Length of Column1 
--c2 = Length of Column2 

ISNULL(NULLIF(CONVERT(VARBINARY(cl), LTRIM(RTRIM(Column1))), CONVERT(VARBINARY(c2),LTRIM(RTRIM(Column2)))), NULLIF(CONVERT(VARBINARY(c2),LTRIM(RTRIM(Column2))), CONVERT(VARBINARY(c1),LTRIM(RTRIM(Column1))))) IS NULL 

È possibile modificare la fine di espressione per IS NOT NULL per il controllo condizione disuguale.

Spero che questo aiuto.

1

È possibile utilizzare la coalescenza in server sql per impostare il valore predefinito di una colonna su un valore non nullo. Coalesce restituisce il primo valore non nullo nell'elenco.

UPDATE table1 
SET  a.field1 = b.field3 
FROM table1 a , 
     table2 b 
WHERE a.field2 = b.field2 
     AND (
      coalesce(a.field1,-1) <> coalesce(b.field3, -1) 
     ) 

Ho assunto che il tuo tipo sia il numero, sebbene tu possa utilizzare altri tipi di dati. Ho anche ipotizzato che se entrambi i valori fossero NULL, le due righe sono equivalenti.

0

Un altro modo sarebbe quello di utilizzare la funzione CHECKSUM

create table #temp 
    (
    val1 varchar(255), 
    val2 varchar(255) 
) 

    insert into #temp values(NULL, NULL) 
    insert into #temp values(NULL, 'B') 
    insert into #temp values('A', NULL) 
    insert into #temp values('A', 'B') 
    insert into #temp values('A', 'A') 

    select *, 
    'Are Not Equal' = case 
    when val1 <> val2 or checksum(val1) <> checksum(val2) then 'true' 
    else 'false' end 
    from #temp