Pourquoi une erreur de requête avec un jeu de résultats vide dans SQL Server 2012?

31

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
DavidN
la source

Réponses:

39

Un premier regard sur les plans d'exécution montre que l'expression 1/0est définie dans les opérateurs Compute Scalar:

Plans graphiques

Maintenant, même si les plans d'exécution commencent à s'exécuter à l'extrême gauche, appelant de manière itérative Openet des GetRowméthodes 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 définies que par un calcul scalaire, avec une évaluation différée jusqu'à une date ultérieure l'opération nécessite le résultat :

Les opérateurs de calcul scalaire qui apparaissent dans les plans d'exécution générés par SET STATISTICS XML peuvent ne pas contenir l'élément RunTimeInformation.  Dans les plans graphiques, les lignes réelles, les reliures réelles et les rembobinages réels peuvent être absents de la fenêtre Propriétés lorsque l'option Inclure le plan d'exécution réel est sélectionnée dans SQL Server Management Studio.  Lorsque cela se produit, cela signifie que bien que ces opérateurs aient été utilisés dans le plan de requête compilé, leur travail a été effectué par d'autres opérateurs dans le plan de requête au moment de l'exécution.  Notez également que le nombre d'exécutions dans la sortie Showplan générée par SET STATISTICS PROFILE est équivalent à la somme des rebinds et rewinds dans Showplans générés par SET STATISTICS XML.  De: MSDN Books Online

Dans ce cas, l'expression résultat de n'est nécessaire que lors de l'assemblage de la ligne pour le retour au client (que vous pouvez penser se produire au vertSELECT icône ). 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 en tant que constantes d'exécution et ainsi é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):

Showplan XML

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 ici, 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:

Expression constante

Pour plus d'informations: Calculer les scalaires, les expressions et les performances

Paul White dit GoFundMonica
la source
21

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:

  1. Analyser l'index de clé primaire
  2. Recherchez les valeurs dans la table de données nécessaires à la requête

Il choisit ce chemin car vous avez une whereclause 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:

  1. Effectuez une analyse complète de la table des données et récupérez les valeurs nécessaires

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éplacera les conditions de laselect 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 la documentation de SQL Server elle-même qui dit que la whereclause 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 de l' whereavant avant selectsignifierait (entre autres choses) "ne pas générer d' selecterreurs de clause sur les lignes non retournées à l'utilisateur".

Gordon Linoff
la source
1
+1 aucun indice si vous avez raison, mais la meilleure réponse que je puisse voir étant donné que la seule différence est la clé primaire.
1
@GordonLinoff Paul Randal vient de confirmer via Twitter que votre réponse était en vogue.
SchmitzIT
4
@Still, l'ordre d'exécution réel, aussi différent soit-il, ne devrait pas conduire à des messages d'erreur comme celui-là.
ypercubeᵀᴹ
7
@ypercube Erland Sommarskog serait d' accord avec vous (élément Connect)
Paul White dit GoFundMonica
2
Merci pour le pointeur - Je me suis connecté et j'ai voté pour la demande.
Gordon Linoff