2009-10-23 6 views
1

J'ai une table, appelons-la History. La clé primaire (aka Clustered Index) est appelée HIST_ID. La table contient environ 2300 lignes dans la base de développement. Considérons maintenant les deux questions suivantes:Cas particulier avec SQL Server, indices et paramètres

Requête 1:

declare @x int 
set @x = 14289 

select * from History where [email protected] 

Requête 2:

declare @x int 
set @x = 14289 

select * from History where [email protected] or @x is null 

La seule différence est le or @x is null à la fin. Cependant, la première requête recherche un index, le second - index scan. Ce qui donne?

La réponse préemptive - non, option (recompiler) n'aide pas.

Ajouté: Je voudrais des faits argumentés solides, pas des suppositions. Je peux deviner une douzaine de raisons possibles pour moi-même. Mais quel est le problème réel ici?

Répondre

0

Je suppose que l'optimiseur détermine qu'il est bénéfique. L'alternative serait d'utiliser le même plan que si vous aviez écrit

select * from History where [email protected] 
union all 
select * from History where @x is null 

Vous pouvez réécrire la requête de cette façon, mais je suis sûr que l'optimiseur est capable de le faire par lui-même. Combien de valeurs nulles avez-vous?

Editer: Il s'avère que j'ai mal lu la question et pensé que vous vouliez WHERE (@x = hist_id OU hist_id est nul). En fait vous voulez un critère dynamique. Découvrez this article. Notez que votre type de requête était censé fonctionner dans SQL2k8 si vous spécifiez WITH (RECOMPILE), mais en raison d'un bogue, ce support a été supprimé.

+0

Ahem, regardez bien! Le "est nul" est fait contre @x, pas la table! C'est une expression FAUX constante! –

+0

La deuxième instruction SELECT dans son union ** retournera ** toujours un ensemble vide. La valeur de @x est 14289, pas null. –

+0

Le support a été ajouté après la publication de la mise à jour cumulative 5. Si vous obtenez la dernière mise à jour, cela fonctionne comme annoncé. – NotMe

1

Je suggérerais que le plan soit produit séparément du paramètre qui est passé/utilisé, donc en substance il y a une exigence (en fonction de la valeur de @x) pour retourner chaque ligne. En tant que tel le plan de requête traite son pire scénario des paramètres qu'il peut recevoir.

par exemple. Si l'entrée de @x était nulle, la requête serait obligée de renvoyer chaque ligne car chaque ligne satisferait une équation littérale/un prédicat qui retournerait toujours vrai. Pour que le plan de requête couvre toutes les valeurs de @x, il doit générer un plan qui effectue une analyse.

+0

J'ai lu que MSSQL peut faire quelque chose appelé "reniflage de paramètres" qui devrait résoudre ce problème. Option (recompiler) en fait devrait faire exactement cela - mais ce n'est pas le cas. –

+0

Je suis sûr qu'il y a une description plus technique de ce concept qui utilise le terme SARG, mais cette version est un peu plus intelligible. –

+0

La fonction de reniflage des paramètres tente de transformer les valeurs littérales d'une requête en paramètres, de sorte qu'il y a plus d'occurrences de cache de plan de requête. Vous passez déjà dans un paramètre, pas un littéral, ce qui le renifle pas le problème/donc une recompilation ne va pas le modifier. – Andrew

0

Bien sûr c'est un balayage d'index. Un balayage d'index clusterisé = analyse de table car vous n'avez pas de prédicat sensible pour "@x IS NULL".

Le plan paramétré en mémoire cache est général et fonctionnera pour @x = NULL ou @x = valeur. Si vous ne définissez pas @x, vous devriez obtenir le même plan.

Si vous avez codé "12345 IS NULL", cela est détecté et ignoré.

Je ne trouve pas d'article de blog sur la façon dont les constantes sont traitées dans les plans de requête. L'essentiel est qu'ils sont généralisés et que les courts-circuits ne permettent pas de réutiliser le plan.