Estimation de la cardinalité pour> = et> pour la valeur des statistiques intra-étape

9

J'essaie de comprendre comment SQL Server essaie d'estimer les clauses «supérieur à» et «supérieur à égal à» dans SQL Server 2014.

Je pense que je comprends l'estimation de la cardinalité quand elle atteint l'étape par exemple si je le fais

    select * from charge where charge_dt >= '1999-10-13 10:47:38.550'

L'estimation de la cardinalité est 6672, qui peut être facilement calculée comme 32 (EQ_ROWS) + 6624 (RANGE_ROWS) + 16 (EQ_ROWS) = 6672 (histogramme dans la capture d'écran ci-dessous)

entrez la description de l'image ici

Mais quand je fais

    select * from charge where charge_dt >= '1999-10-13 10:48:38.550' 

(augmenté le temps à 10:48 donc ce n'est pas une étape)

l'estimation est 4844.13.

Comment est-ce calculé?

jesijesi
la source

Réponses:

9

La seule difficulté est de décider comment gérer les étapes de l'histogramme partiellement couvertes par l'intervalle de prédicat de requête. Les étapes de l'histogramme entier couvert par la plage de prédicats sont triviales comme indiqué dans la question.

Estimateur de cardinalité hérité

F = fraction (entre 0 et 1) de la plage de pas couverte par le prédicat de requête.

L'idée de base est d'utiliser F(interpolation linéaire) pour déterminer combien de valeurs distinctes intra-étapes sont couvertes par le prédicat. La multiplication de ce résultat par le nombre moyen de lignes par valeur distincte (en supposant l'uniformité) et l'ajout de lignes égales à l'étape donne l'estimation de cardinalité:

Cardinalité = EQ_ROWS + (AVG_RANGE_ROWS * F * DISTINCT_RANGE_ROWS)

La même formule est utilisée pour >et >=dans l'héritage CE.

Nouvel estimateur de cardinalité

Le nouveau CE modifie légèrement l'algorithme précédent pour différencier entre >et >=.

En prenant d' >abord, la formule est:

Cardinalité = EQ_ROWS + (AVG_RANGE_ROWS * (F * (DISTINCT_RANGE_ROWS - 1))))

Car >=c'est:

Cardinalité = EQ_ROWS + (AVG_RANGE_ROWS * ((F * (DISTINCT_RANGE_ROWS - 1)) + 1))

Le + 1indique que lorsque la comparaison implique l'égalité, une correspondance est supposée (l'hypothèse d'inclusion).

Dans l'exemple de question, Fpeut être calculé comme suit:

DECLARE 
    @Q datetime = '1999-10-13T10:48:38.550',
    @K1 datetime = '1999-10-13T10:47:38.550',
    @K2 datetime = '1999-10-13T10:51:19.317';

DECLARE
    @QR float = DATEDIFF(MILLISECOND, @Q, @K2), -- predicate range
    @SR float = DATEDIFF(MILLISECOND, @K1, @K2) -- whole step range

SELECT
    F = @QR / @SR;

Le résultat est 0,728219019233034 . Brancher cela dans la formule pour >=avec les autres valeurs connues:

Cardinalité = EQ_ROWS + (AVG_RANGE_ROWS * ((F * (DISTINCT_RANGE_ROWS - 1)) + 1))
            = 16 + (16,1956 * ((0,728219019233034 * (409 - 1)) + 1))
            = 16 + (16,1956 * ((0,728219019233034 * 408) + 1))
            = 16 + (16,1956 * (297,113359847077872 + 1))
            = 16 + (16,1956 * 298,113359847077872)
            = 16 + 4828,1247307393343837632
            = 4844.1247307393343837632
            = 4844.12473073933 (pour une précision flottante)

Ce résultat est conforme à l'estimation de 4844,13 indiquée dans la question.

La même requête utilisant l'ancien CE (par exemple en utilisant l'indicateur de trace 9481) devrait produire une estimation de:

Cardinalité = EQ_ROWS + (AVG_RANGE_ROWS * F * DISTINCT_RANGE_ROWS)
            = 16 + (16,1956 * 0,728219019233034 * 409)
            = 16 + 4823,72307468722
            = 4839,72307468722

Notez que l'estimation serait la même pour >et en >=utilisant l'ancien CE.

Paul White 9
la source
4

La formule d'estimation des lignes devient un peu maladroite lorsque le filtre est "supérieur à" ou "inférieur à", mais c'est un nombre auquel vous pouvez arriver.

Les nombres

En utilisant l'étape 193, voici les numéros pertinents:

RANGE_ROWS = 6624

EQ_ROWS = 16

AVG_RANGE_ROWS = 16.1956

RANGE_HI_KEY de l'étape précédente = 1999-10-13 10: 47: 38.550

RANGE_HI_KEY de l'étape actuelle = 1999-10-13 10: 51: 19.317

Valeur de la clause WHERE = 1999-10-13 10: 48: 38.550

La formule

1) Trouvez le ms entre les deux touches hi de gamme

SELECT DATEDIFF (ms, '1999-10-13 10:47:38.550', '1999-10-13 10:51:19.317')

Le résultat est de 220767 ms.

2) Ajustez le nombre de lignes

Nous devons trouver les lignes par milliseconde, mais avant de le faire, nous devons soustraire AVG_RANGE_ROWS de RANGE_ROWS:

6624 - 16,1956 = 6607,8044 rangées

3) Calculez les lignes par ms avec le nombre de lignes ajusté:

6607.8044 lignes / 220767 ms = 0,0299311 lignes par ms

4) Calculez le ms entre la valeur de la clause WHERE et l'étape courante RANGE_HI_KEY

SELECT DATEDIFF (ms, '1999-10-13 10:48:38.550', '1999-10-13 10:51:19.317')

Cela nous donne 160767 ms.

5) Calculez les lignes de cette étape en fonction des lignes par seconde:

0,0299311 lignes / ms * 160767 ms = 4811,9332 lignes

6) Rappelez-vous comment nous avons soustrait AVG_RANGE_ROWS plus tôt? Il est temps de les rajouter. Maintenant que nous avons fini de calculer les nombres liés aux lignes par seconde, nous pouvons également ajouter EQ_ROWS en toute sécurité:

4811.9332 + 16.1956 + 16 = 4844.1288

Arrondi, c'est notre estimation 4844.13.

Test de la formule

Je n'ai trouvé aucun article ou article de blog expliquant pourquoi AVG_RANGE_ROWS est soustrait avant le calcul des lignes par ms. Je suis en mesure de confirmer qu'ils sont pris en compte dans l'estimation, mais seulement à la dernière milliseconde - littéralement.

En utilisant la base de données WideWorldImporters , j'ai effectué des tests incrémentiels et trouvé la diminution des estimations de lignes linéaire jusqu'à la fin de l'étape, où 1x AVG_RANGE_ROWS est soudainement pris en compte.

Voici mon exemple de requête:

SELECT PickingCompletedWhen
FROM Sales.Orders
WHERE PickingCompletedWhen >= '2016-05-24 11:00:01.000000'

J'ai mis à jour les statistiques de PickingCompletedWhen, puis j'ai obtenu l'histogramme:

DBCC SHOW_STATISTICS([sales.orders], '_WA_Sys_0000000E_44CA3770')

Histogramme pour _WA_Sys_0000000E_44CA3770 (3 dernières étapes)

Pour voir comment les lignes estimées diminuent à l'approche de la RANGE_HI_KEY, j'ai collecté des échantillons tout au long de l'étape. La diminution est linéaire, mais se comporte comme si un nombre de lignes égal à la valeur AVG_RANGE_ROWS ne faisait tout simplement pas partie de la tendance ... jusqu'à ce que vous atteigniez la RANGE_HI_KEY et soudainement, elles chutent comme une dette non recouvrée annulée. Vous pouvez le voir dans les exemples de données, en particulier dans le graphique.

entrez la description de l'image ici

Notez la baisse régulière des lignes jusqu'à ce que nous atteignions le RANGE_HI_KEY puis le BOOM que le dernier bloc AVG_RANGE_ROWS soit subitement soustrait. Il est également facile de repérer un graphique.

entrez la description de l'image ici

Pour résumer, le traitement étrange d'AVG_RANGE_ROWS rend le calcul des estimations de ligne plus complexe, mais vous pouvez toujours concilier ce que fait le CE.

Qu'en est-il de l'interruption exponentielle?

L'arrêt exponentiel est la méthode que le nouveau (à partir de SQL Server 2014) Cardinality Estimator utilise pour obtenir de meilleures estimations lors de l'utilisation de plusieurs statistiques sur une seule colonne. Étant donné que cette question concernait une seule statistique de colonne, elle n'implique pas la formule EB.

Doug Lane
la source