Share via


Risoluzione dei problemi relativi agli errori e agli avvisi nelle espressioni di query

In alcuni casi in SQL Server 2008 le espressioni incluse nelle query vengono valutate prima rispetto a SQL Server 2000. Tale funzionamento offre i vantaggi seguenti:

  • Gli indici delle colonne calcolate possono essere abbinati alle espressioni di una query uguali all'espressione della colonna calcolata.

  • Viene impedito il calcolo ridondante dei risultati delle espressioni.

In base alla tipologia della query e dei dati del database, è tuttavia possibile che si verifichino eccezioni di run-time in SQL Server 2008 se per la query è disponibile un'espressione non sicura. Tali eccezioni includono:

  • Eccezioni aritmetiche, ad esempio divisione per zero, overflow e underflow.

  • Errori di conversione, ad esempio perdita di precisione e un tentativo di convertire una stringa non numerica in un numero.

  • Aggregazione per un set di valori non tutti garantiti come valori non Null.

In SQL Server 2000 queste stesse eccezioni potrebbero invece non verificarsi in casi specifici di applicazioni che utilizzano dati. La presenza di un piano di query modificato in seguito alla variazione delle statistiche potrebbe tuttavia generare un'eccezione in SQL Server 2008. Per prevenire queste eccezioni di run-time, modificare la query in modo da includere espressioni condizionali quali NULLIF o CASE.

Nota importanteImportante

Le espressioni contenute in una condizione di ricerca, in un elenco di selezione o in qualsiasi altra posizione all'interno di una query possono essere suddivise e quindi riorganizzate in una o più espressioni indipendenti. Tali espressioni indipendenti possono essere valutate in SQL Server in qualsiasi ordine l'una rispetto all'altra. Le operazioni di filtro, inclusi i join, non vengono necessariamente eseguite prima del calcolo delle colonne di risultati.

Nell'esempio seguente, l'espressione x/y nell'elenco di selezione può essere valutata in qualsiasi momento, anche per le righe che non vengono considerate output per la query.

USE tempdb
GO
IF OBJECT_ID('T','U') IS NOT NULL
    DROP TABLE T
IF OBJECT_ID('S','U') IS NOT NULL
    DROP TABLE S
GO
CREATE TABLE T(x float, y float, z nvarchar(30))
CREATE TABLE S(a float, b float)
GO
INSERT INTO T VALUES (1, 0, 'unknown')
INSERT INTO T VALUES(1, 2, '10')
GO
INSERT INTO S VALUES (1, 1)
INSERT INTO S VALUES (1, 2)
INSERT INTO S VALUES (1, 3)
INSERT INTO S VALUES (1, 4)
INSERT INTO S VALUES (1, 5)

L'esecuzione della query seguente non riesce in SQL Server 2008 e ha invece esito positivo in SQL Server 2000.

SELECT x/y FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)

La query ha esito negativo perché l'espressione x/y provoca un errore di divisione per zero quando viene valutata per y=0.

Di seguito è illustrata una soluzione che consente di eseguire la query correttamente:

SELECT x/NULLIF(y,0) FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)

L'espressione NULLIF(y,0) restituisce NULL se y = 0. In caso contrario, restituisce il valore per y. L'espressione x/NULL restituisce NULL e non si verifica alcuna eccezione.

Si consideri l'esempio seguente relativo alla conversione di dati di tipo carattere in tipi numerici.

SELECT CONVERT(tinyint, z) FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)

La query ha esito negativo e genera un errore di conversione quando tenta di convertire la stringa 'unknown' in un tipo di dati tinyint. Un modo per risolvere il problema consiste nel modificare la query in modo che esegua la conversione solo se z è di tipo numeric, specificando un'istruzione CASE del tipo:

SELECT CASE WHEN ISNUMERIC(z) = 1
    THEN CONVERT(tinyint, z) 
    ELSE 0 
END
FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)

Un seconda soluzione consiste nell'evitare di utilizzare il valore di stringa speciale 'unknown' nel database e invece utilizzare NULL. Una terza soluzione consiste nel modificare il tipo della colonna z in tinyint per evitare la conversione del tutto. Soluzioni di questo tipo richiedono rispettivamente modifiche ai dati e allo schema e, pertanto, una quantità di interventi maggiore rispetto alla modifica della query. È tuttavia possibile prenderle in considerazione se semplificano la scrittura di altre query.

Avviso di input NULL generato dalle funzioni di aggregazione

Le funzioni di aggregazione, ad esempio MIN, visualizzano un messaggio di avviso relativo all'eliminazione di un valore NULL se il relativo input contiene un valore NULL. Questo avviso può dipendere dal piano di esecuzione. Per evitare che gli input NULL nell'aggregazione vengano elaborati e che venga visualizzato un avviso, è possibile modificare la query a livello locale per eliminare i valori Null. Si consideri l'istruzione SELECT nell'esempio seguente:

USE tempdb
GO
IF OBJECT_ID('newtitles','U') IS NOT NULL
....DROP TABLE newtitles 
GO
CREATE TABLE dbo.newtitles 
   (title varchar (80) NULL ,
    pubdate datetime NULL)
GO
INSERT dbo.newtitles VALUES('Title 1', NULL)
INSERT dbo.newtitles VALUES('Title 2', '20050311')
GO
SELECT t.title, t.pubdate, m.min_pubdate
FROM newtitles AS t,
   (SELECT MIN(pubdate) AS min_pubdate 
    FROM newtitles) AS m
WHERE t.pubdate = m.min_pubdate
GO

In SQL Server 2008 questa query genera un avviso. Per evitare la visualizzazione dell'avviso, modificare la query aggiungendo la condizione WHERE pubdate IS NOT NULL che consente di escludere tramite un filtro i valori Null prima dell'aggregazione:

SELECT t.title, t.pubdate, m.min_pubdate
FROM newtitles AS t,
   (SELECT MIN(pubdate) AS min_pubdate 
    FROM newtitles
    WHERE pubdate IS NOT NULL) AS m
WHERE t.pubdate = m.min_pubdate
GO