Dépannage des erreurs et des avertissements dans les expressions de requêtes
Parfois, SQL Server 2005 évalue les expressions dans les requêtes plus tôt que SQL Server 2000. Ce comportement offre de gros avantages :
- Il est possible de faire correspondre des index sur des colonnes calculées avec des expressions d'une requête qui sont identiques à l'expression de la colonne calculée.
- Le calcul redondant des résultats d'expression est empêché.
Toutefois, selon la nature de la requête et les données de la base de données, des exceptions à l'exécution peuvent se produire dans SQL Server 2005 si la requête contient une expression incertaine. Ces exceptions à l'exécution peuvent être :
- Des exceptions arithmétiques : division par zéro, débordement et dépassement de précision.
- Des échecs de conversion tels qu'une perte de précision et une tentative de convertir une chaîne non numérique en nombre
- L'agrégation sur un ensemble de valeurs qui ne sont pas garanties comme étant non NULL.
Dans SQL Server 2000, il arrive que ces exceptions ne se produisent pas dans une application spécifique utilisant des données spécifiques. Cependant, un plan de requête qui est modifié en raison de statistiques variables peut conduire à une exception dans SQL Server 2005. Vous pouvez éviter ces exceptions d'exécution en modifiant la requête pour qu'elle inclue des expressions conditionnelles telles que NULLIF ou CASE.
Important : |
---|
Des expressions qui apparaissent dans une condition de recherche, une liste de sélection ou à tout autre emplacement dans une requête peuvent être décomposées et réorganisées en une ou plusieurs expressions indépendantes. SQL Server peut évaluer ces expressions indépendantes dans n'importe quel ordre l'une par rapport à l'autre Les opérations de filtrage, y compris les jointures, ne sont pas nécessairement appliquées avant que les colonnes du résultat soient calculées. |
Dans l'exemple suivant, l'expression x/y
dans la liste de sélection peut être évaluée à tout moment, même pour les lignes qui en fin de compte ne sont pas retenues pour les résultats de la requête.
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)
La requête suivante échoue dans SQL Server 2005 mais se termine dans SQL Server 2000.
SELECT x/y FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)
La requête échoue parce que l'expression x/y
provoque une erreur de division par zéro lors de l'évaluation de l'expression pour y=0
.
Le code suivant représente une solution qui permet d'exécuter correctement la requête :
SELECT x/NULLIF(y,0) FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)
L'expression NULLIF(y,0)
retourne NULL
si y = 0
. Dans le cas contraire, l'expression retourne la valeur pour y
. L'expression x/NULL
renvoie NULL
et aucune exception ne se manifeste.
Considérez l'exemple suivant qui implique la conversion de données de caractères en types numériques.
SELECT CONVERT(tinyint, z) FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)
La requête échoue avec une erreur de conversion lorsqu'elle essaie de convertir la chaîne 'unknown'
en une valeur tinyint
. Un moyen de résoudre ce problème consiste à modifier la requête pour effectuer la conversion uniquement si z
est de type numeric
, en introduisant une instruction CASE
:
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)
Une autre solution consiste à renoncer à utiliser la valeur de chaîne spéciale 'unknown'
dans la base de données pour utiliser NULL
à la place. Une troisième solution consiste à remplacer le type de données de la colonne z par tinyint pour éviter complètement la conversion. Comme ces solutions impliquent des modifications de données et de schéma, leur application respective peut demander plus de travail que la modification de la requête. Toutefois, vous pouvez envisager ces solutions si elles facilitent également l'écriture d'autres requêtes.
Avertissement d'entrée NULL émis par les fonctions d'agrégation
Les fonctions d'agrégation, telles que MIN, émettent un avertissement indiquant qu'une valeur NULL a été éliminée si leur entrée contient une valeur NULL. Cet avertissement peut dépendre du plan. Si vous ne souhaitez pas que les valeurs NULL en entrée dans l'agrégat soient traitées ni qu'un avertissement soit émis, vous pouvez modifier votre requête localement pour éliminer les valeurs NULL. Considérez l'instruction SELECT
dans l'exemple suivant :
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
Dans SQL Server 2005, cette requête génère un avertissement. Pour empêcher l'avertissement, modifiez la requête en ajoutant la condition WHERE pubdate IS NOT NULL
pour éliminer les valeurs NULL avant l'agrégation :
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