Используя SQL2008, я пытаюсь найти эффективный запрос для поиска строки, дата которой ближе всего к определенной целевой дате.
Существуют очевидные неэффективные решения (например, сканирование таблицы с использованием ABS и DATEDIFF ), на которые я не потрудился посмотреть, потому что в моей таблице уже есть индекс покрытия, где дата - первый столбец. Я могу сузить результаты, используя этот индекс, прежде чем точно определить, какая строка является ближайшей.
Теоретически я должен быть в состоянии удовлетворить запрос, используя поиск по одному индексу, а затем последовательное извлечение 2 строк данных из этого индекса.
Но до сих пор я не смог найти более оптимального решения, чем это:
DECLARE @target DATETIME = '01/02/2011'
SELECT TOP 1 Val, Measured
FROM (
SELECT TOP 1 Val, Measured
FROM tbl
WHERE Measured <= @Target
ORDER BY Measured desc
UNION ALL
SELECT TOP 1 Val, Measured
FROM tbl
WHERE Measured >= @Target
ORDER BY Measured asc
) x
ORDER BY ABS (DATEDIFF (second, Measured, @Target))
Это быстро (4 логических чтения в схеме тестирования ниже, 9 логических чтений в моей реальной таблице), но это по-прежнему решение с 2 сканированиями. Есть ли более эффективное решение, которое попадает в этот индекс только один раз?
Или мое существующее решение является "достаточно хорошим", потому что при втором поиске по индексу будут извлекаться кэшированные страницы, к которым обращается первый запрос, а значит, это будет настолько быстро, что дальнейшая оптимизация (даже если это возможно) приведет к минимальному фактическому улучшению производительности?
Вот схема и некоторые примеры данных. И то, и другое упрощено по сравнению с моей реальной схемой, хотя полученный план запроса такой же, как и у моей более сложной таблицы:
CREATE TABLE tbl
(
ID int IDENTITY(1,1) PRIMARY KEY CLUSTERED NOT NULL,
Measured DATETIME NOT NULL,
Val int NOT NULL
);
CREATE NONCLUSTERED INDEX IX_tbl ON tbl (Measured) INCLUDE (Val)
INSERT tbl VALUES ('2011-01-01 12:34',6);
INSERT tbl VALUES ('2011-01-01 23:34',6);
INSERT tbl VALUES ('2011-01-03 09:03',12);
INSERT tbl VALUES ('2011-02-01 09:24',18);
INSERT tbl VALUES ('2011-02-08 07:12',7);
INSERT tbl VALUES ('2011-03-01 12:34',6);
INSERT tbl VALUES ('2011-04-03 09:03',12);
INSERT tbl VALUES ('2011-05-01 09:24',18);
INSERT tbl VALUES ('2011-06-08 07:12',7);
-- insert another few million rows here to compare to my real-world table