Il piano di esecuzione NON utilizza INDEX, utilizza la scansione della tabella


9

So che quando si tratta di utilizzare un indice o una scansione di tabelle, SQL Server utilizza le statistiche per vedere quale è meglio.

Ho una tabella con 20 milioni di righe. Ho un indice su (SnapshotKey, Measure) e questa query:

select Measure, SnapshotKey, MeasureBand
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

La query restituisce 500k righe. Quindi la query seleziona solo il 2,5% delle righe della tabella.

La domanda è perché SQL Server non utilizza l'indice non cluster che ho e utilizza invece una scansione di tabella?

Le statistiche sono aggiornate.

Buono a dirsi che le prestazioni della query sono buone però.

Scansione tabella

Scansione tabella

Indice forzato

Force Index

Struttura tabella / indice

CREATE TABLE [t1](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [date] NOT NULL,
    [Measure] [nvarchar](30) NOT NULL,
    [MeasureBand] [nvarchar](30) NOT NULL,
    -- and many more fields
) ON [PRIMARY]

Nessun PK sul tavolo, in quanto è un data warehouse.

CREATE NONCLUSTERED INDEX [nci_SnapshotKeyMeasure] ON [t1]
(
    [SnapshotKey] ASC,
    [Measure] ASC
)

Risposte:


16

La ricerca dell'indice potrebbe non essere la scelta migliore se si restituiscono molte righe e / o le righe sono molto ampie. Le ricerche possono essere costose se l'indice non copre. Vedi # 2 qui .

Nel tuo scenario, Query Optimizer stima che l'esecuzione di 50.000 ricerche individuali sarà più costosa di una singola scansione. La scelta dell'ottimizzatore tra scan e seek (con ricerche RID per le colonne necessarie alla query, ma non presente nell'indice non cluster) si basa sul costo stimato di ciascuna alternativa.

L'ottimizzatore sceglie sempre l'alternativa più economica che considera. Se si esamina la proprietà Costo sottotree stimato nel nodo principale dei due piani di esecuzione, si noterà che il piano di scansione ha un costo stimato inferiore rispetto al piano di ricerca. Di conseguenza, l'ottimizzatore ha scelto la scansione. Questa è essenzialmente la risposta alla tua domanda.

Ora, il modello di costo utilizzato dall'ottimizzatore si basa su ipotesi e "numeri magici" che difficilmente corrispondono alle caratteristiche prestazionali del sistema. In particolare, un'ipotesi fatta nel modello è che la query inizia l'esecuzione con nessuno dei dati richiesti o pagine dell'indice già in memoria. Un altro è che l'I / O sequenziale (previsto per una scansione) è più economico del modello I / O casuale assunto per le ricerche RID. Ci sono molte altre ipotesi e avvertenze, troppe per approfondire qui.

Tuttavia, è stato dimostrato che il modello di costo nel suo insieme produce piani generalmente "abbastanza buoni" per la maggior parte delle query, sulla maggior parte degli schemi di database, sulla maggior parte delle configurazioni hardware, il più delle volte, ovunque. È un bel risultato, se ci pensate.

Limitazioni del modello e altri fattori a volte significano che l'ottimizzatore sceglie un piano che, di fatto, non è "abbastanza buono". Riferisci che "le prestazioni sono buone", quindi non sembra essere il caso qui.


9

In realtà hai 595.947 righe corrispondenti, che rappresentano circa il 3% dei tuoi dati. Quindi il costo della ricerca si somma rapidamente. Supponiamo di avere 100 righe per pagina nella tabella, ovvero 200.000 pagine da leggere in una scansione della tabella. È molto più economico che fare 595.947 ricerche.

Con la GROUP BYclausola nella domanda, penso che starai meglio con un tasto composito su (Measure, SnapshotKey, MeasureBand).

Guarda il suggerimento "indice mancante". Ti dice di includere colonne per evitare le ricerche. Più in generale, se si fa riferimento ad altre colonne nella query, dovranno trovarsi nelle chiavi o nella INCLUDEclausola del nuovo indice. Altrimenti sarà comunque necessario eseguire le 595.947 ricerche per ottenere quei valori.

Ad esempio, per la query:

select Measure, SnapshotKey, MeasureBand, SUM(NumLoans), SUM(PrinBal)
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

... avresti bisogno di:

CREATE INDEX ixWhatever 
ON t1 (Measure, SnapshotKey, MeasureBand) 
INCLUDE (NumLoans,PrinBal);

6
  1. Il campo nella condizione WHERE non è il campo iniziale dell'indice.

  2. Hai measuredefinito come NVARCHAR in modo prefisso letterale con N: where Measure = N'FinanceFICOScore'.

Prendi in considerazione la creazione di un indice cluster su SnapshotKey. Se è unico, può essere un PK (e cluster). Se non univoco, non può essere un PK, ma può comunque essere un indice cluster non univoco. Quindi il tuo indice non cluster sarebbe solo sulla measurecolonna.

E, considerando che anche il primo campo GROUP BYè measure, ciò trarrebbe beneficio anche dall'essere measureil campo principale.

In effetti, per questa operazione, potrebbe essere necessario definire l'indice non cluster su Measure, SnapshotKey, MeasureBand, nell'ordine esatto in cui corrisponde alla GROUP BYclausola. MeasureBandDal punto di vista dimensionale, si sta solo aggiungendo poiché l'indice non cluster è già basato su Measureed MeasureKeyè già incluso nell'indice poiché è ora la chiave dell'indice cluster (no, Measurenon verrà duplicato nell'indice non cluster).

@Rob aveva menzionato in un commento ora cancellato la sua risposta che risolvere questo problema richiede solo che l'indice non cluster sia definito con questi tre campi in questo ordine e che la creazione di un indice cluster (non univoco) su SnapshotKeynon sia necessaria . Anche se probabilmente ha ragione (speravo che funzionassero meno campi), direi comunque che avere l'indice cluster è vantaggioso non solo per questa operazione, ma probabilmente per la maggior parte degli altri.


La discussione su questa risposta è stata spostata in chat .
Paul White 9
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.