Stima della cardinalità per> = e> per il valore delle statistiche intra step


9

Sto cercando di capire come SQL Server tenta di stimare le clausole 'maggiore di' e 'maggiore di uguale a' dove in SQL Server 2014.

Penso di capire la stima della cardinalità quando colpisce il passo, ad esempio se lo faccio

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

La stima della cardinalità è 6672 che può essere facilmente calcolata come 32 (EQ_ROWS) + 6624 (RANGE_ROWS) + 16 (EQ_ROWS) = 6672 (istogramma nella schermata seguente)

inserisci qui la descrizione dell'immagine

Ma quando lo faccio

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

(aumentato il tempo a 10:48 quindi non è un passo)

la stima è 4844.13.

Come viene calcolato?

Risposte:


9

L'unica difficoltà è decidere come gestire i passaggi dell'istogramma parzialmente coperti dall'intervallo di predicato della query. Gli interi passaggi dell'istogramma coperti dall'intervallo predicato sono banali, come indicato nella domanda.

Stimatore di cardinalità legacy

F = frazione (tra 0 e 1) dell'intervallo di passi coperto dal predicato della query.

L'idea di base è utilizzare F(interpolazione lineare) per determinare quanti valori distinti intra-step sono coperti dal predicato. Moltiplicando questo risultato per il numero medio di righe per valore distinto (assumendo l'uniformità) e aggiungendo il passaggio uguale a righe si ottiene la stima della cardinalità:

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

La stessa formula viene utilizzata per >e >=nell'eredità CE.

Nuovo stimatore della cardinalità

Il nuovo CE modifica leggermente l'algoritmo precedente per differenziare tra >e >=.

Prendendo >prima, la formula è:

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

Perché >=è:

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

La + 1riflette che quando il confronto implica uguaglianza, una partita viene assunto (presupposto inclusione).

Nell'esempio della domanda, Fpuò essere calcolato come:

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;

Il risultato è 0.728219019233034 . Inserendolo nella formula per >=con gli altri valori noti:

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 (per galleggiare la precisione)

Questo risultato concorda con la stima di 4844.13 mostrata nella domanda.

La stessa query che utilizza il CE legacy (ad es. Utilizzando trace flag 9481) dovrebbe produrre una stima di:

Cardinalità = EQ_ROWS + (AVG_RANGE_ROWS * F * DISTINCT_RANGE_ROWS)
            = 16 + (16.1956 * 0.728219019233034 * 409)
            = 16 + 4823.72307468722
            = 4839.72307468722

Nota che la stima sarebbe la stessa per >e >=usando il CE legacy.


4

La formula per stimare le righe diventa un po 'sciocca quando il filtro è "maggiore di" o "minore di", ma è un numero a cui puoi arrivare.

I numeri

Usando il passo 193, ecco i numeri rilevanti:

RANGE_ROWS = 6624

EQ_ROWS = 16

AVG_RANGE_ROWS = 16.1956

RANGE_HI_KEY dal passaggio precedente = 1999-10-13 10: 47: 38.550

RANGE_HI_KEY dal passaggio corrente = 1999-10-13 10: 51: 19.317

Valore dalla clausola WHERE = 1999-10-13 10: 48: 38.550

La formula

1) Trova il ms tra i due tasti della gamma

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

Il risultato è 220767 ms.

2) Regola il numero di righe

Dobbiamo trovare le righe per millisecondo, ma prima di farlo, dobbiamo sottrarre AVG_RANGE_ROWS da RANGE_ROWS:

6624 - 16.1956 = 6607.8044 righe

3) Calcola le righe per ms con il numero corretto di righe:

6607.8044 righe / 220767 ms = .0299311 righe per ms

4) Calcola il ms tra il valore della clausola WHERE e il passaggio corrente RANGE_HI_KEY

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

Questo ci dà 160767 ms.

5) Calcola le righe in questo passaggio in base alle righe al secondo:

.0299311 righe / ms * 160767 ms = 4811.9332 righe

6) Ricordi come abbiamo sottratto AVG_RANGE_ROWS in precedenza? È ora di aggiungerli di nuovo. Ora che abbiamo finito di calcolare i numeri relativi alle righe al secondo, possiamo tranquillamente aggiungere anche EQ_ROWS:

4811.9332 + 16.1956 + 16 = 4844.1288

Arrotondato, questa è la nostra stima 4844.13.

Test della formula

Non sono riuscito a trovare articoli o post sul blog sul motivo per cui AVG_RANGE_ROWS viene sottratto prima che vengano calcolate le righe per ms. Sono stato in grado di confermare che sono stati contabilizzati nel preventivo, ma solo all'ultimo millisecondo - letteralmente.

Usando il database WideWorldImporter , ho fatto alcuni test incrementali e ho scoperto che la diminuzione delle stime delle righe era lineare fino alla fine del passaggio, dove improvvisamente viene contabilizzato 1x AVG_RANGE_ROWS.

Ecco la mia query di esempio:

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

Ho aggiornato le statistiche per PickingCompletedWhen, quindi ho ottenuto l'istogramma:

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

Istogramma per _WA_Sys_0000000E_44CA3770 (ultimi 3 passaggi)

Per vedere come diminuiscono le righe stimate mentre ci avviciniamo a RANGE_HI_KEY, ho raccolto campioni durante tutto il passaggio. La riduzione è lineare, ma si comporta come se un numero di righe pari al valore AVG_RANGE_ROWS non facesse parte del trend ... fino a quando non si preme RANGE_HI_KEY e all'improvviso cadono come un debito non riscosso cancellato. Puoi vederlo nei dati di esempio, specialmente nel grafico.

inserisci qui la descrizione dell'immagine

Notare il costante declino delle righe fino a quando non si preme RANGE_HI_KEY e quindi BOOM che l'ultimo blocco AVG_RANGE_ROWS viene sottratto all'improvviso. È anche facile individuarlo in un grafico.

inserisci qui la descrizione dell'immagine

Per riassumere, lo strano trattamento di AVG_RANGE_ROWS rende più complesso il calcolo delle stime delle righe, ma è sempre possibile riconciliare ciò che sta facendo il CE.

Che dire del backoff esponenziale?

Exponential Backoff è il metodo utilizzato dal nuovo Estimatore di cardinalità (a partire da SQL Server 2014) per ottenere stime migliori quando si utilizzano più statistiche a colonna singola. Poiché questa domanda riguardava una statistica a colonna singola, non implica la formula EB.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.