Sandro Bizioli


Chi sogna di giorno conosce molte cose che sfuggono a chi sogna soltanto di notte. (E.A.Poe)
Mappa

SQL e i valori NULL

Leggendo qua e là sui vari newsgroup, mi sono accorto che una delle principali fonti di confusione è data dall'utilizzo, in SQL dei NULLs.
Cerchiamo di fare un po' di luce su questo "oscuro" concetto..
Che cosa è un NULL?
Nel linguaggio SQL (ANSI-SQL) si definisce come NULL quel valore speciale che identifica un'assenza di valore.
Secondo lo standard ANSI-92 il NULL non è un "dato", ma piuttosto indica che si è tralasciato il dato stesso.
Facciamo un esempio: in una tabella indicante Nome, Cognome e Città

USE tempdb
-- Definisco la tabella myTable
CREATE TABLE myTable
        (
        Nome varchar(25) NOT NULL,
        Cognome varchar(25) NOT NULL,
   Citta varchar(25) NULL
        )  ON [PRIMARY]
GO
-- La popolo
SET NOCOUNT ON
INSERT dbo.myTable VALUES('Sandro', 'Bizioli', 'Brescia')
INSERT dbo.myTable VALUES('Mario',  'Rossi',   'Milano')
INSERT dbo.myTable VALUES('Andrea', 'Ferro',   NULL)
SET NOCOUNT OFF
-- Seleziono i dati
Select Nome, Cognome, Citta FROM myTable
-- Elimino la tabella
Drop table myTable

/* OUTPUP
Nome                     Cognome              Citta
---------------------- ---------------------- ----------------------
Sandro                   Bizioli              Brescia
Mario                    Rossi                    
Andrea                   Ferro                NULL

(3 row(s) affected)
*/

Quello che notiamo è che la città di Sandro è Brescia, mentre la colonna che identifica la città di Mario, contiene una stringa di lunghezza zero.
E' importante, però, capire che sia per Sandro che per Mario, noi conosciamo il valore.
Per Andrea le cose non sono così semplici, perchè la colonna città contiene NULL che indica la nostra NON conoscenza del fatto che ci sia oppure no una città di appartenenza.
Un primo consiglio è quello di utilizzare esclusivamente i NULL per indicare la nostra effettiva "non conoscenza" di un valore e mai in sostituzione di valori a zero, o di stringhe a lunghezza zero (blank).
Fin qui sembra che le cose siano abbastanza gestibili e che non ci siano particolari problemi se non di organizzazione logica dei nostri dati. Purtroppo non è così, perchè i veri problemi li avremo nel momento in cui andremo ad interrogare tabelle e campi contenenti valori NULL.
Ad esempio, supponiamo di confrontare tra loro due valori boolean, TRUE e FALSE:

TRUE  AND TRUE  = TRUE
TRUE  AND FALSE = FALSE
FALSE AND FALSE = FALSE
FALSE AND TRUE  = FALSE

TRUE  OR  TRUE   = TRUE
TRUE  OR  FALSE  = TRUE
FALSE OR  FALSE  = FALSE
FALSE OR  TRUE   = TRUE

NOT TRUE  = FALSE
NOT FALSE = TRUE

Le cose cambiano notevolmente se introduciamo il concetto di "non conosciuto", NULL per l'appunto.
Lo Standard ANSI-92 SQL, indica che se uno degli operatori è NULL il risultato della comparazione sarà sempre "Sconosciuto"
TRUE  AND NULL  = Unknown
FALSE AND NULL  = Unknown
NULL  AND TRUE  = Unknown
NULL  AND FALSE = Unknown
NULL  AND NULL  = Unknown

Facciamo un esempio concreto:
USE tempdb
-- Definisco la tabella myTable
CREATE TABLE myTable
        (
        Nome varchar(25) NOT NULL,
        Cognome varchar(25) NOT NULL,
     Citta varchar(25) NULL
        )  ON [PRIMARY]
-- La popolo
SET NOCOUNT ON
INSERT dbo.myTable VALUES('Sandro', 'Bizioli', 'Brescia')
INSERT dbo.myTable VALUES('Mario', 'Rossi', '')
INSERT dbo.myTable VALUES('Andrea', 'Ferro', NULL)
INSERT dbo.myTable VALUES('Paolo', 'Bianchi', 'Milano')
INSERT dbo.myTable VALUES('Luca', 'Verdi', 'Torino')
INSERT dbo.myTable VALUES('Marco', 'Grandi', NULL)
SET NOCOUNT OFF
-- Estraggo i valori
SELECT * 
FROM myTable
WHERE Citta IS NULL
/* OUTPUP
Nome                    Cognome               Citta
---------------------- ---------------------- ----------------------
Andrea                  Ferro                 NULL
Marco                   Grandi                NULL
(2 row(s) affected)
*/
-- Estraggo i valori
SELECT * 
FROM myTable
WHERE Citta = NULL
/* OUTPUP
Nome                   Cognome                Citta
---------------------- ---------------------- ----------------------
(0 row(s) affected)
*/
-- Elimino la tabella
Drop table myTable

Come è possibile notare nella prima select le righe restituite sono 2, mentre nel secondo caso nessuna: come sostenuto dallo Standard ANSI-92 SQL, se uno degli operatori è NULL il risultato sarà "sconosciuto".
Particolare attenzione anche alle clausole COUNT() che potrebbero comportarsi in modo anomalo:
-- Estraggo i valori
SELECT count(*) 
FROM myTable
/* Output
6
*/
SELECT count(citta) 
FROM myTable
/* Output
4
*/

Nel primo caso count(*) restituisce il numero totale di righe, mentre count(citta) conta il numero delle riche che posseggono citta, ovviamente escludendo i valori NULL!
Attenzione che se voglio ottenere tutte il totale delle righe che hanno citta a NULL, non dovrò usare count(citta), bensì count(*)
SELECT count(citta) 
FROM myTable
WHERE citta is NULL
/* Output
0
*/
SELECT count(*) 
FROM myTable
WHERE citta is NULL
/* Output
2
*/

In ANSI SQL, i NULL non sono uguali e comparabili a nulla, neppure ad altri NULL. Il confronto restituirà sempre come valore SCONOSCIUTO.
 
Per verificare, ulteriormente, questa situazione scriviamo:
SET ANSI_NULLS ON
DECLARE @mioVal CHAR(4)
SET @mioVal = NULL
If @mioVal = NULL
     PRINT 'TRUE'
ELSE IF NOT(@mioVal = NULL)
     PRINT 'FALSE'
ELSE
     PRINT 'UNKNOWN'
/* OUTPUT
UNKNOWN
*/

Nell'istruzione IF sono stati verificati entrambi i casi e cioè se NULL = NULL se NULL <> NULL, ma entrambi hanno dato esito negativo entrando nell'ultimo ELSE che indica, per l'appunto, l'impossibilità di confronto.
Da notare il fatto che tale regola sussiste solo se si rispetta lo Standard ANSI-92 SQL, infatti, se cambiamo la prima riga a "SET ANSI_NULLS OFF" il risultato diverrà "TRUE".
Questa impostazione pregiudica la portabilità del codice SQL su altri sistemi creando confusione là dove, invece, lo Standard Ansi è rispettato.
E' decisamente consigliabile non modificare l'impostazione ANSI_NULLS ON!

Uguale attenzione va fatta nelle operazioni matematiche: qualora uno degli operatori fosse un NULL il risultato sarà sempre NULL.
SELECT 1 + 2 + NULL + 3
/* OUTPUT
NULL
*/

Ricordiamoci, inoltre, che nel momento in cui dichiariamo una variabile, essa assume il valore NULL fino a quando non la inizializziamo attraverso l'istruzione SET.
Scrivere:
Declare @mioVal as char(4)
Set @mioVal = NULL
Select @mioVal
/* OUTPUT 
NULL
*/

sarà perfettamente uguale allo scrivere:
Declare @mioVal as char(4)
Select @mioVal
/* OUTPUT 
NULL
*/

Non è neppure possibile utilizzare una variabile valorizzata a NULL al posto della clausola IS NULL poichè verrebbe sollevato un errore di sintassi non valida!
DECLARE @myVal AS Char(4)
SET @myVal = NULL
SELECT * 
FROM myTable
WHERE Citta IS @myVal
Categoria: SQL Server
martedì, 14 mar 2006 Ore. 15.38
Statistiche
  • Views Home Page: 111.555
  • Views Posts: 568.321
  • Views Gallerie: 117.624
  • n° Posts: 227
  • n° Commenti: 222
Copyright © 2002-2007 - Blogs 2.0
dotNetHell.it | Home Page Blogs
ASP.NET 2.0 Windows 2003