Ich gehe davon aus, dass Sie Daten verzerrt haben, dass Sie keine Abfragehinweise verwenden möchten, um das Optimierungsprogramm zu erzwingen, was zu tun ist, und dass Sie eine gute Leistung für alle möglichen Eingabewerte von benötigen @Id
. Sie können einen Abfrageplan erstellen, für den garantiert nur einige wenige logische Lesevorgänge für einen möglichen Eingabewert erforderlich sind, wenn Sie bereit sind, das folgende Indexpaar (oder das entsprechende) zu erstellen:
CREATE INDEX GetMinSomeTimestamp ON dbo.MyTable (Id, SomeTimestamp) WHERE SomeBit = 1;
CREATE INDEX GetMaxSomeInt ON dbo.MyTable (Id, SomeInt) WHERE SomeBit = 1;
Unten sind meine Testdaten. Ich habe 13 M Zeilen in die Tabelle eingefügt und dafür gesorgt, dass die Hälfte einen Wert '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
für die Id
Spalte hat.
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;
Diese Abfrage könnte auf den ersten Blick etwas seltsam aussehen:
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;
Es nutzt die Reihenfolge der Indizes, um den minimalen oder maximalen Wert mit wenigen logischen Lesevorgängen zu ermitteln. Das CROSS JOIN
ist es korrekte Ergebnisse zu erhalten , wenn es keine passenden Zeilen für den sind @Id
Wert. Selbst wenn ich nach dem beliebtesten Wert in der Tabelle filtere (6,5 Millionen Zeilen), erhalte ich nur 8 logische Lesevorgänge:
Tabelle 'MyTable'. Scananzahl 2, logische Lesevorgänge 8
Hier ist der Abfrageplan:
Beide Indexsuchen finden 0 oder 1 Zeilen. Es ist äußerst effizient, aber das Erstellen von zwei Indizes könnte für Ihr Szenario überfordert sein. Sie könnten stattdessen den folgenden Index in Betracht ziehen:
CREATE INDEX CoveringIndex ON dbo.MyTable (Id) INCLUDE (SomeTimestamp, SomeInt) WHERE SomeBit = 1;
Jetzt MAXDOP 1
sieht der Abfrageplan für die ursprüngliche Abfrage (mit einem optionalen Hinweis) etwas anders aus:
Die Schlüsselsuchen sind nicht mehr erforderlich. Mit einem besseren Zugriffspfad, der für alle Eingaben gut funktionieren sollte, sollten Sie sich keine Sorgen machen müssen, dass der Optimierer aufgrund des Dichtevektors den falschen Abfrageplan auswählt. Diese Abfrage und dieser Index sind jedoch nicht so effizient wie die andere, wenn Sie nach einem beliebten @Id
Wert suchen .
Tabelle 'MyTable'. Scananzahl 1, logische Lesevorgänge 33757