web-dev-qa-db-fra.com

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

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
31
DavidN

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:

Graphical plans

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 :

Compute Scalar operators that appear in Showplans generated by SET STATISTICS XML might not contain the RunTimeInformation element. In graphical Showplans, Actual Rows, Actual Rebinds, and Actual Rewinds might be absent from the Properties window when the Include Actual Execution Plan option is selected in SQL Server Management Studio. When this occurs, it means that although these operators were used in the compiled query plan, their work was performed by other operators in the run-time query plan. Also note that the number of executes in Showplan output generated by SET STATISTICS PROFILE is equivalent to the sum of rebinds and rewinds in Showplans generated by SET STATISTICS XML. From: MSDN Books Online

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):

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

Constant Expression

Plus d'informations: Calculer les scalaires, les expressions et les performances

39
Paul White 9

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

  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é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" ".

21
Gordon Linoff