Lors de l'exécution des requêtes suivantes dans MS SQL Server 2012, la deuxième requête échoue mais pas la première. En outre, lorsqu'elle est exécutée sans les clauses where, les deux requêtes échouent. Je ne comprends pas pourquoi l'un ou l'autre échouerait, car les deux devraient avoir des ensembles de résultats vides. Toute aide/compréhension est appréciée.
create table #temp
(id int primary key)
create table #temp2
(id int)
select 1/0
from #temp
where id = 1
select 1/0
from #temp2
where id = 1
Un premier regard sur les plans d'exécution montre que l'expression 1/0
est défini dans les opérateurs de calcul scalaire:
Maintenant, même si les plans d'exécution commencent à s'exécuter à l'extrême gauche, appelant de manière itérative les méthodes Open
et GetRow
sur les itérateurs enfants pour renvoyer des résultats, SQL Server 2005 et versions ultérieures contiennent une optimisation selon laquelle les expressions ne sont souvent que défini par un calculateur scalaire, avec évaluation différée jusqu'à ce qu'une opération ultérieure nécessite le résultat :
Dans ce cas, l'expression result n'est nécessaire que lors de l'assemblage de la ligne pour le retour au client (ce que vous pouvez penser se produire à l'icône verte SELECT
). Selon cette logique, une évaluation différée signifierait que l'expression n'est jamais évaluée car aucun des deux plans ne génère de ligne de retour. Pour travailler un peu le point, ni la recherche d'index clusterisé ni l'analyse de table ne renvoient une ligne, il n'y a donc pas de ligne à assembler pour le retour au client.
Cependant, il existe une optimisation distincte par laquelle certaines expressions peuvent être identifiées comme constantes d'exécution et donc évaluées une fois avant le début de l'exécution de la requête . Dans ce cas, une indication que cela s'est produit peut être trouvée dans le showplan XML (Clustered Index Seek plan à gauche, Plan Scan Table à droite):
J'ai écrit plus sur les mécanismes sous-jacents et comment ils peuvent affecter les performances dans ce billet de blog . En utilisant les informations fournies, nous pouvons modifier la première requête afin que les deux expressions soient évaluées et mises en cache avant le début de l'exécution:
select 1/0 * CONVERT(integer, @@DBTS)
from #temp
where id = 1
select 1/0
from #temp2
where id = 1
À présent, le premier plan contient également une référence d'expression constante et les deux requêtes produisent le message d'erreur. Le XML de la première requête contient:
Plus d'informations: Calculer les scalaires, les expressions et les performances
Je vais deviner intelligemment (et dans le processus, attirer probablement un gourou de SQL Server qui pourrait donner une réponse vraiment détaillée).
La première requête aborde l'exécution comme:
Il choisit ce chemin car vous avez une clause where
sur la clé primaire. Il n'atteint jamais la deuxième étape, la requête n'échoue donc pas.
Le second n'a pas de clé primaire pour s'exécuter, il aborde donc la requête comme suit:
L'une de ces valeurs est 1/0
à l'origine du problème.
Ceci est un exemple de SQL Server optimisant la requête. Pour la plupart, c'est une bonne chose. SQL Server déplace les conditions de select
dans l'opération d'analyse de table. Cela enregistre souvent des étapes dans l'évaluation de la requête.
Mais cette optimisation n'est pas une bonne chose non atténuée. En fait, il semble violer SQL Server documentation lui-même qui dit que la clause where
est évaluée avant le select
. Eh bien, ils pourraient avoir une explication savante de ce que cela signifie. Pour la plupart des humains, cependant, le traitement logique du where
avant le select
signifierait (entre autres) "ne génère pas d'erreurs de clause select
- sur les lignes non retournées à l'utilisateur" ".