web-dev-qa-db-fra.com

quelqu'un peut-il m'aider avec ce plan de requête horrible?

La requête:

    SELECT Object1.Column1, Object2.Column2 AS Column3, Object2.Column4 AS Column5, 
Object3.Column6, Object3.Column7,Object1.Column8, Object1.Column9, 
Object1.Column10, Object1.Column11, Object1.Column12, Object1.Column13, 
Object1.Column14, Object1.Column15 as Column15, Object1.Column16, 
Object4.Column4 AS Column17, Object4.Column2 AS Column18, Object1.Column19,
 Object1.Column20, Object1.Column21, Object1.Column22, Object1.Column23, 
Object1.Column24, Object1.Column25, Object1.Column26, Object5.Column4, 
Object1.Column27, Object1.Column28, Object1.Column29, Object3.Column30, 
Object3.Column1 as Column31, Object3.Column32 as Column33, Object1.Column34 
as Column34, ? AS Column35 , Object3.Column36 as Column37 
FROM Object6 AS Object1 
INNER JOIN Object7 AS Object3 ON Object1.Column38 = Object3.Column1 
INNER JOIN Object8 AS Object2 ON Object3.Column30 = Object2.Column1 
LEFT JOIN Object9 AS Object4 ON Object1.Column16 = Object4.Column2 
LEFT JOIN Object10 AS Object5 ON Object1.Column9 = Object5.Column2 
WHERE Object2.Column1 <> ? AND Object1.Column8 = ? 
AND ( coalesce(Column16,?)= ? ) 
AND EXISTS (
         SELECT ? 
         FROM Object11 
         WHERE Column39 = ? 
         AND Column30 = Object3.Column30) 
ORDER BY Column7 desc
OFFSET ? ROWS FETCH FIRST ? ROWS ONLY

voici le plan de requête

Je sais que je devrais peut-être ajouter un index à ce sujet:

Database1.Schema1.Object7.Column30, Database1.Schema1.Object7.Column36, Database1.Schema1.Object7.Column6, Database1.Schema1.Object7.Column32

mais l'une de ces colonnes est un varchar 4000 et elle ne peut pas être créée en raison de la grande dimension du champ.

J'ai remarqué qu'il ne faut 25 secondes que si les lignes renvoyées sont inférieures au premier numéro de récupération

7
Gabriele D'Onufrio

enter image description here

Le plan d'exécution accède d'abord à Object7 En utilisant un index non couvrant dans l'ordre de Column7. Il effectue ensuite des recherches clés sur cette table et joint les boucles imbriquées aux autres tables, la dernière jointure aboutissant à l'opérateur TOP toujours ordonné par Column7.

Une fois qu'il a reçu suffisamment de lignes pour satisfaire le OFFSET ... FETCH, Il peut cesser de demander d'autres lignes aux opérateurs en aval. SQL Server estime qu'il n'aura besoin de lire que 2419 lignes de l'index initial sur Object7.Column7 Avant d'arriver à ce point.

Cette estimation n'est pas du tout correcte. En fait, il finit par lire l'intégralité de Object7 Et manque probablement de lignes avant que OFFSET ... FETCH Ne soit satisfait.

La semi-jointure sur Object11 Réduit le nombre de lignes de près de moitié, mais le tueur est la jointure sur Object6 Et le prédicat sur la même table. Ensemble, ceux-ci réduisent les 9,753,116 Lignes sortant de la semi-jointure à 2.

Vous pouvez essayer de passer un peu de temps à regarder les statistiques sur les tables impliquées pour essayer d'obtenir des estimations de cardinalité à partir de ces jointures pour être plus précises ou bien vous pouvez ajouter OPTION (USE HINT ('DISABLE_OPTIMIZER_ROWGOAL') ) pour que le plan soit chiffré sans l'hypothèse qu'il peut arrêtez-vous tôt en raison du OFFSET ... FETCH - cela vous donnera certainement un plan différent.

9
Martin Smith

Si vous pouvez ajouter un index sur Object11, Column39 + Column30 et un index sur Object7, Column30, avec d'autres champs d'Object7 dans la partie INCLUDE du CREATE INDEX instruction pour Object 7, vous devriez avoir une augmentation importante des performances. Il s'agit de la grande majorité des dépenses en ressources impliquées dans cette requête.

Sur la base du XML du plan, ceux-ci semblent être proches des index optimaux pour cette requête:

CREATE INDEX Idx_Object11_Column39_Column30
ON Object11(Column39_Column30)

CREATE INDEX Idx_Object7_Column30_Column1_Includes
ON Object7 (Column30, Column1)
INCLUDE (Column7, Column36, Column6, Column2)
1
Laughing Vergil