Transact-SQL: NOT IN in a nested select

Occorre fare attenzione alla clausola NOT IN, quando l’insieme dei dati in cui viene eseguito il controllo è una select a sua volta. Se la SELECT interna contiene valori null non viene restituito nessun record.
Qualche query su due tabelle di esempio:

CREATE TABLE TABELLA1 (id INT, descr VARCHAR(11))
INSERT INTO tabella1 VALUES (1, 'pippo')
INSERT INTO tabella1 VALUES (1, 'pluto')
INSERT INTO tabella1 VALUES (1, 'paperino')
ID DESCR
1 pippo
1 pluto
1 paperino
CREATE TABLE TABELLA2 (id INT, descr VARCHAR(11))
INSERT INTO tabella2 VALUES (2, 'pippo')
INSERT INTO tabella2 VALUES (2, 'pluto')
ID DESCR
2 pippo
2 pluto

Se voglio prendere tutti i record di TABELLA1 che non hanno una descrizione in TABELLA2 posso scrivere così:

SELECT *
  FROM tabella1 t1
 WHERE t1.descr NOT IN (SELECT t2.descr FROM tabella2 t2)

E ottengo il risultato:

ID DESCR
1 paperino

che è coerente con la query. Adesso inserisco una riga con valore null nel campo DESCR in TABELLA2;

INSERT INTO tabella2 VALUES (2,NULL)

Ora la stessa query restituisce un recordset vuoto, sbagliando.

Per continuare ad usare il NOT IN occorre inserire la condizione di IS NOT NULL alla select interna:

SELECT *
  FROM tabella1 t1
 WHERE t1.descr NOT IN (SELECT t2.descr FROM tabella2 t2 WHERE t2.descr IS NOT NULL)

Ora il recordset è di nuovo coerente con la query.

Una alternativa al NOT IN è il JOIN diretto fra le tabelle:

SELECT t1.* 
  FROM tabella1 t1 LEFT JOIN tabella2 t2 ON (t1.descr = t2.descr)
 WHERE t2.descr IS NULL

oppure utilizzando la clausola EXCEPT:

SELECT descr FROM tabella1 EXCEPT SELECT descr FROM tabella2

Il problema, ad onore del vero, non è solo di SQLServer, ma è condiviso anche da altri DBMS.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>