Я припускаю, що ви перекосили дані, що ви не хочете використовувати підказки запитів, щоб змусити оптимізатора, що робити, і що вам потрібно отримати хороші показники для всіх можливих вхідних значень @Id
. Ви можете отримати план запитів із гарантією, що вимагає лише декількох логічних зчитувань для будь-якого можливого вхідного значення, якщо ви готові створити наступну пару індексів (або їх еквівалент):
CREATE INDEX GetMinSomeTimestamp ON dbo.MyTable (Id, SomeTimestamp) WHERE SomeBit = 1;
CREATE INDEX GetMaxSomeInt ON dbo.MyTable (Id, SomeInt) WHERE SomeBit = 1;
Нижче наведені мої дані тесту. Я помістив 13 М рядків у таблицю, і половина з них має значення '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
для Id
стовпця.
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;
Цей запит спочатку може виглядати трохи дивним:
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;
Він створений для того, щоб скористатися впорядкованістю індексів для знаходження мінімального чи максимального значення з кількома логічними показаннями. Це CROSS JOIN
є для отримання правильних результатів, коли для цього @Id
значення немає відповідних рядків . Навіть якщо я фільтрую найпопулярніше значення таблиці (відповідає 6,5 мільйона рядків), я отримую лише 8 логічних зчитувань:
Таблиця "MyTable". Кількість сканувань 2, логічні зчитування 8
Ось план запитів:
Обидва показники шукають знаходження 0 або 1 рядків. Це надзвичайно ефективно, але створити два індекси може бути надмірним для вашого сценарію. Ви можете замість цього розглянути наступний індекс:
CREATE INDEX CoveringIndex ON dbo.MyTable (Id) INCLUDE (SomeTimestamp, SomeInt) WHERE SomeBit = 1;
Тепер план запиту оригінального запиту (з необов'язковим MAXDOP 1
підказом) виглядає дещо інакше:
Пошук ключів більше не потрібний. З кращим доступом до доступу, який повинен працювати добре для всіх входів, вам не доведеться турбуватися про те, що оптимізатор вибирає неправильний план запитів через вектор густини. Однак цей запит та індекс не будуть настільки ефективними, як інші, якщо ви шукаєте популярне @Id
значення.
Таблиця "MyTable". Кількість сканувань 1, логічне зчитування 33757