Presumo che tu abbia dati distorti, che non desideri utilizzare i suggerimenti per le query per forzare l'ottimizzatore su cosa fare e che devi ottenere buone prestazioni per tutti i possibili valori di input di @Id
. È possibile ottenere un piano di query che richieda solo alcune manciate di letture logiche per ogni possibile valore di input se si è disposti a creare la seguente coppia di indici (o il loro equivalente):
CREATE INDEX GetMinSomeTimestamp ON dbo.MyTable (Id, SomeTimestamp) WHERE SomeBit = 1;
CREATE INDEX GetMaxSomeInt ON dbo.MyTable (Id, SomeInt) WHERE SomeBit = 1;
Di seguito sono riportati i miei dati di test. Ho inserito 13 M righe nella tabella e ho fatto in modo che metà di esse avesse un valore '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
per la Id
colonna.
DROP TABLE IF EXISTS dbo.MyTable;
CREATE TABLE dbo.MyTable (
Id uniqueidentifier,
SomeTimestamp DATETIME2,
SomeInt INT,
SomeBit BIT,
FILLER VARCHAR(100)
);
INSERT INTO dbo.MyTable WITH (TABLOCK)
SELECT NEWID(), CURRENT_TIMESTAMP, 0, 1, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
INSERT INTO dbo.MyTable WITH (TABLOCK)
SELECT '3A35EA17-CE7E-4637-8319-4C517B6E48CA', CURRENT_TIMESTAMP, 0, 1, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Questa query potrebbe sembrare inizialmente un po 'strana:
DECLARE @Id UNIQUEIDENTIFIER = '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
SELECT
@Id,
st.SomeTimestamp,
si.SomeInt
FROM (
SELECT TOP (1) SomeInt, Id
FROM dbo.MyTable
WHERE Id = @Id
AND SomeBit = 1
ORDER BY SomeInt DESC
) si
CROSS JOIN (
SELECT TOP (1) SomeTimestamp, Id
FROM dbo.MyTable
WHERE Id = @Id
AND SomeBit = 1
ORDER BY SomeTimestamp ASC
) st;
È progettato per sfruttare l'ordinamento degli indici per trovare il valore minimo o massimo con alcune letture logiche. L' CROSS JOIN
è lì per ottenere risultati corretti quando non sono presenti righe corrispondenti per il @Id
valore. Anche se filtro sul valore più popolare nella tabella (corrispondente a 6,5 milioni di righe) ottengo solo 8 letture logiche:
Tabella "MyTable". Conteggio scansioni 2, letture logiche 8
Ecco il piano di query:
Entrambe le ricerche dell'indice trovano 0 o 1 righe. È estremamente efficiente, ma la creazione di due indici potrebbe essere eccessiva per il tuo scenario. È possibile invece considerare il seguente indice:
CREATE INDEX CoveringIndex ON dbo.MyTable (Id) INCLUDE (SomeTimestamp, SomeInt) WHERE SomeBit = 1;
Ora il piano di query per la query originale (con un MAXDOP 1
suggerimento opzionale ) ha un aspetto leggermente diverso:
Le ricerche chiave non sono più necessarie. Con un percorso di accesso migliore che dovrebbe funzionare bene per tutti gli input, non dovresti preoccuparti che l'ottimizzatore scelga il piano di query errato a causa del vettore di densità. Tuttavia, questa query e questo indice non saranno efficienti quanto l'altro se si cerca un @Id
valore popolare .
Tabella "MyTable". Conteggio scansione 1, lettura logica 33757