J'ai eu ce problème il y a longtemps, j'ai trouvé une solution de contournement qui me convenait et je l'ai oubliée.
Mais maintenant, il y a cette question sur SO donc je suis prêt à soulever ce problème.
Il y a une vue qui joint quelques tables de manière très simple (commandes + lignes de commande).
Lorsqu'elle est interrogée sans where
clause, la vue renvoie plusieurs millions de lignes.
Cependant, personne ne l'appelle jamais comme ça. La requête habituelle est
select * from that_nasty_view where order_number = 123456;
Cela renvoie environ 10 enregistrements sur 5m.
Une chose importante: la vue contient une fonction de fenêtre rank()
, qui est partitionnée exactement par le champ à l'aide duquel la vue est toujours interrogée:
rank() over (partition by order_number order by detail_line_number)
Maintenant, si cette vue est interrogée avec des paramètres littéraux dans la chaîne de requête, exactement comme indiqué ci-dessus, elle retourne les lignes instantanément. Le plan d'exécution est très bien:
- Recherche d'index sur les deux tables en utilisant les indices sur
order_number
(retourne 10 lignes). - Calcul des fenêtres sur le minuscule résultat renvoyé.
- Sélection.
Cependant, lorsque la vue est appelée de manière paramétrée, les choses deviennent désagréables:
Index scan
sur toutes les tables en ignorant les indices. Renvoie 5 m de lignes.- Énorme jointure.
- Calcul des fenêtres sur tous les
partition
s (environ 500k fenêtres). Filter
prendre 10 rangs sur 5m.- Sélectionner
Cela se produit dans tous les cas lorsque des paramètres sont impliqués. Il peut s'agir de SSMS:
declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;
Il peut s'agir d'un client ODBC, tel qu'Excel:
select * from that_nasty_view where order_number = ?
Ou il peut s'agir de tout autre client qui utilise des paramètres et non la concaténation sql.
Si la fonction de fenêtre est supprimée de la vue, elle s'exécute parfaitement rapidement, qu'elle soit ou non interrogée avec des paramètres.
Ma solution de contournement consistait à supprimer la fonction incriminée et à la réappliquer ultérieurement.
Mais qu'est-ce qui donne? Est-ce vraiment un bug dans la façon dont SQL Server 2008 gère les fonctions des fenêtres?
order_number
n'est pas une clé primaire. C'estint not null
avec un index non clusterisé dans les deux tables.OPTION (RECOMPILE)
aide?Réponses:
Cela semble être un problème de longue date qui continue de refaire surface sous une forme ou une autre et qui est toujours présent dans SQL Server 2012.
Certains messages en discutant sont
Toutes les versions actuelles de SQL Server jusqu'en 2012 inclus ne sont pas en mesure de pousser le filtre sur un groupe de partitionnement après le projet de séquence pour un prédicat paramétré, sauf s'il
option(recompile)
est utilisé (si 2008+).Une alternative à l'
recompile
indice serait de réécrire la requête pour utiliser un TVF en ligne paramétré comme suggéré par @ a1ex07)la source
J'essaierais de remplacer la vue par un udf de valeur table. De cette façon, il filtrera d'abord les enregistrements, puis appliquera la fonction de fenêtre. Cette fonction peut accepter le paramètre de table de sorte que vous pouvez passer plusieurs
order_number
dans cela source
SELECT * FROM my_funct(12345)
it will filter records first, and then apply window function
est incorrect. Il n'y a pas d'ordre d'exécution déterministe