Les plans de requête avec des filtres bitmap peuvent parfois être difficiles à lire. Extrait de l'article BOL pour les flux de répartition (soulignement le mien):
L'opérateur Repartition Streams consomme plusieurs flux et produit plusieurs flux d'enregistrements. Le contenu et le format de l'enregistrement ne sont pas modifiés. Si l'optimiseur de requêtes utilise un filtre bitmap, le nombre de lignes dans le flux de sortie est réduit.
De plus, un article sur les filtres bitmap est également utile:
Lors de l'analyse d'un plan d'exécution contenant un filtrage bitmap, il est important de comprendre comment les données circulent dans le plan et où le filtrage est appliqué. Le filtre bitmap et le bitmap optimisé sont créés du côté de l'entrée de génération (la table de dimension) d'une jointure de hachage; cependant, le filtrage réel est généralement effectué dans l'opérateur de parallélisme, qui se trouve du côté de l'entrée de sonde (la table de faits) de la jointure de hachage. Cependant, lorsque le filtre bitmap est basé sur une colonne entière, le filtre peut être appliqué directement à l'opération de table ou d'indexation initiale plutôt qu'à l'opérateur de parallélisme. Cette technique est appelée optimisation en ligne.
Je crois que c'est ce que vous observez avec votre requête. Il est possible de proposer une démo relativement simple pour montrer un opérateur de flux de répartition réduisant une estimation de cardinalité, même lorsque l'opérateur bitmap est IN_ROW
contre la table de faits. Préparation des données:
create table outer_tbl (ID BIGINT NOT NULL);
INSERT INTO outer_tbl WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;
create table inner_tbl_1 (ID BIGINT NULL);
create table inner_tbl_2 (ID BIGINT NULL);
INSERT INTO inner_tbl_1 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
INSERT INTO inner_tbl_2 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Voici une requête que vous ne devez pas exécuter:
SELECT *
FROM outer_tbl o
INNER JOIN inner_tbl_1 i ON o.ID = i.ID
INNER JOIN inner_tbl_2 i2 ON o.ID = i2.ID
OPTION (HASH JOIN, QUERYTRACEON 9481, QUERYTRACEON 8649);
J'ai téléchargé le plan . Jetez un œil à l'opérateur près de inner_tbl_2
:
Vous pouvez également trouver utile le deuxième test dans Hash Joins on Nullable Columns de Paul White.
Il existe certaines incohérences dans la façon dont la réduction des lignes est appliquée. Je n'ai pu le voir que dans un plan avec au moins trois tables. Cependant, la réduction des lignes attendues semble raisonnable avec la bonne distribution des données. Supposons que la colonne jointe dans la table de faits ait de nombreuses valeurs répétées qui ne sont pas présentes dans la table de dimension. Un filtre bitmap peut éliminer ces lignes avant qu'elles n'atteignent la jointure. Pour votre requête, l'estimation est entièrement réduite à 1. La façon dont les lignes sont réparties entre la fonction de hachage fournit un bon indice:
Sur cette base, je soupçonne que vous avez beaucoup de valeurs répétées pour la Object1.Column21
colonne. Si les colonnes répétées ne se trouvent pas dans l'histogramme des statistiques, Object4.Column19
alors SQL Server pourrait obtenir une estimation de cardinalité très erronée.
Je pense que vous devriez être préoccupé par le fait qu'il pourrait être possible d'améliorer les performances de la requête. Bien sûr, si la requête répond aux exigences de temps de réponse ou de SLA, cela peut ne pas valoir la peine d'être approfondi. Cependant, si vous souhaitez approfondir votre recherche, vous pouvez faire certaines choses (autres que la mise à jour des statistiques) pour savoir si l'optimiseur de requêtes choisirait un meilleur plan s'il disposait de meilleures informations. Vous pouvez placer les résultats de la jointure entre Database1.Schema1.Object10
et Database1.Schema1.Object11
dans une table temporaire et voir si vous continuez à obtenir des jointures de boucles imbriquées. Vous pouvez remplacer cette jointure par un LEFT OUTER JOIN
afin que l'optimiseur de requête ne réduise pas le nombre de lignes à cette étape. Vous pouvez ajouter un MAXDOP 1
indice à votre requête pour voir ce qui se passe. Vous pourriez utiliserTOP
avec une table dérivée pour forcer la jointure à durer en dernier, ou vous pouvez même commenter la jointure à partir de la requête. J'espère que ces suggestions suffisent pour vous aider à démarrer.
En ce qui concerne l' élément de connexion dans la question, il est extrêmement peu probable qu'il soit lié à votre question. Ce problème n'a pas à voir avec de mauvaises estimations de ligne. Cela a à voir avec une condition de concurrence critique dans le parallélisme qui provoque le traitement d'un trop grand nombre de lignes dans le plan de requête en arrière-plan. Ici, il semble que votre requête ne fasse aucun travail supplémentaire.