Home > Articoli > Three-Valued Logic: TRUE, FALSE and UNKNOWN

Three-Valued Logic: TRUE, FALSE and UNKNOWN

Il capitolo uno del libro Inside Microsoft SQL Server 2008: T-SQL Querying di Itzik Ben-Gan è davvero ricco di contenuti e dopo aver parlato di uno degli aspetti singolari (che caratterizza il linguaggio SQL) nell’articolo Logical Query Processing, parleremo ora di un altro aspetto altrettanto singolare: I possibili valori che ci possiamo aspettare, in SQL, come risultato della valutazione logica di un’espressione.

In molti linguaggi di programmazione valutando un’espressione logica possiamo aspettarci vengano restituiti solo due possibili valori, l’espressione può essere vera (TRUE) oppure falsa (FALSE). In SQL, un’espressione logica può restituire oltre ai valori TRUE, FALSE anche il valore “sconosciutoUNKNOWN.

Il valore UNKNOWN, in SQL, viene tipicamente restituito quando nell’espressione logica vengono trattati valori NULLs. Se proviamo a valutare le seguenti espressioni il risultato sarà UNKNOWN:

NULL > 50 ?

NULL = NULL ?

(X + NULL) > Y ?

Il NULL rappresenta la mancanza di un valore, quando confrontiamo un valore mancante con un valore esistente, il risultato è sconosciuto UNKNOWN (proprio perché uno dei due valori è mancante e quindi l’espressione non potrà essere ne vera ne falsa). Inoltre, mentre NOT TRUE è FALSE e NOT FALSE è TRUE, l’opposto di un valore sconosciuto è ancora sconosciuto: NOT UNKNOWN è UNKNOWN.

In aggiunta, i valori sconosciuti e i NULLs vengono trattati in modo non omogeneo dai diversi elementi del linguaggio. Ad esempio, tutti i filtri di una query (ON, WHERE e HAVING) trattano UNKNOWN come valore falso (FALSE), di conseguenza le righe per le quali il valore del filtro è UNKNOWN non vengono restituite nel result set della query. Un trattamento diverso viene riservato ai valori sconosciuto valutati in un vincolo di tipo CHECK, in questo caso infatti UNKNOWN è trattato come valore vero (TRUE).

Ipotizziamo di dover implementare un vincolo di tipo CHECK in una colonna di una tabella, per rafforzare l’integrità di dominio e assicurare che vengano inseriti solo valori numerici maggiori di zero (>0). Qualora la condizione del vincolo CHECK si trovasse a dover valutare l’inserimento di un valore NULL, SQL dovrà eseguire il confronto (NULL > 0) ? Come abbiamo detto in precedenza il risultato sarà UNKNOWN e verrà trattato dal vincolo CHECK come valore TRUE.

Con seguente frammento di codice T-SQL viene creata, nel tempdb, la tabella temporanea dbo.tmpUNKNOWN e sulla colonna valore viene implementato il vincolo CHECK chk_valore_greater_zero.

use [tempdb];
go

if OBJECT_ID(‘dbo.tmpUNKNOWN’) is not null
  drop table dbo.tmpUNKNOWN;
go

create table dbo.tmpUNKNOWN
(
  codice varchar(20)
  ,descr varchar(40)
  ,valore decimal(8,2)
);

alter table dbo.tmpUNKNOWN
  add constraint [chk_valore_greater_zero] check (valore > 0);

Inseriamo ora una riga nella tabella temporanea, per la colonna valore specifichiamo NULL.

insert into dbo.tmpUNKNOWN
(
  codice
  ,descr
  ,valore
)
values
(
  ‘TVL’
  ,’Three-Valued Logic’
  ,NULL   — UNKNOWN trattato come True dal vincolo CHECK
);

L’inserimento viene eseguito correttamente, abbiamo dimostrato che il vincolo CHECK tratta i valori sconosciuti UNKNOWN come valori TRUE.

Interroghiamo ora la tabella dbo.tmpUNKNOWN, che risultato ci aspettiamo dalle due query che seguono ? Ci aspettiamo che la riga precedentemente inserita non venga estratta perché la clausola WHERE tratta i valori sconosciuti come valori falsi (FALSE)…

select
  *
from
  dbo.tmpUNKNOWN
where
  (valore <> 0) — ???

select
  *
from
  dbo.tmpUNKNOWN
where
  (valore = NULL) — ???

Il confronto tra due NULL nel filtro WHERE restituisce un valore sconosciuto che viene trattato come FALSE, da qui si potrebbe dedurre che ogni valore NULL è diverso da qualsiasi altro NULL. L’eccezione che conferma questa regola è rappresentata dalle clausole UNIQUE, GROUP BY, ORDER BY e dall’operatore SET che riconoscono i NULLs come valori uguali.

Per ottenere in output la riga precedentemente inserita eseguiamo la query seguente dove l’operatore di confronto “=” è stato sostituito con IS NULL.

select
  *
from
  dbo.tmpUNKNOWN
where
  (valore IS NULL)

Il comportamento, compatibile con lo standard SQL-92, degli operatori di confronto “uguale a” (=) e “diverso da” (<>) quando vengono utilizzati valori NULLs è regolato dall’opzione ANSI_NULLS che appartiene al gruppo ANSI_DEFAULTS; maggiori dettagli sono disponibili in questo post: Quando l’opzione ANSI_NULLS ha effetto su optimizer e prestazioni

 

Conclusioni

E’ sicuramente una buona idea essere a conoscenza del fatto che il risultato di un’espressione logica può essere sconosciuto (UNKNOWN) e che i NULLs sono trattati in modo diverso dai diversi elementi del linguaggio.

 

Pulizia del tempdb

/* Pulizia tempdb */

IF OBJECT_ID(‘dbo.tmpUNKNOWN’) IS NOT NULL DROP TABLE dbo.tmpUNKNOWN;

 

Chi è Sergio Govoni

Sergio Govoni è laureato in Scienze e Tecnologie Informatiche. Da oltre 16 anni lavora presso una software house che produce un noto sistema ERP, distribuito a livello nazionale ed internazionale, multi azienda client/server su piattaforma Win32. Attualmente si occupa di progettazione e analisi funzionale, coordina un team di sviluppo ed è responsabile tecnico di prodotto. Lavora con SQL Server dalla versione 7.0 e si è occupato d'implementazione e manutenzione di database relazionali in ambito gestionale, ottimizzazione delle prestazioni e problem solving. Nello staff di UGISS si dedica alla formazione e alla divulgazione in ambito SQL Server e tecnologie a esso collegate, scrivendo articoli e partecipando come speaker ai workshop e alle iniziative del primo e più importante User Group Italiano sulla tecnologia SQL Server. Ha conseguito la certificazione MCP, MCTS SQL Server. Per il suo contributo nelle comunità tecniche e per la condivisione della propria esperienza con altri, dal 2010 riceve il riconoscimento SQL Server MVP (Microsoft Most Valuable Professional). Nel corso dell'anno 2011 ha contribuito alla scrittura del libro SQL Server MVP Deep Dives Volume 2 (http://www.manning.com/delaney/).

Leggi Anche

Unit testing: Come scrivere la tua prima unit test!

Nell’articolo precedente, il secondo di questa serie, abbiamo descritto come installare il framework tSQLt, il …