эффективный «поиск ближайшего числа или даты» в SQL, где столбец даты / числа покрывается индексом - PullRequest
2 голосов
/ 19 июля 2011

Используя 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

1 Ответ

1 голос
/ 19 июля 2011

Попробуйте сначала определить, где в таблице находится ваш @target, а затем ограничить область поиска +1 / -1 с точностью до дня или недели. Тогда сортировка по дате в пределах этого набора для нахождения ближайшего будет стоить дешевле, чем применение TOP 1 / ORDER BY ко всему набору с каждой стороны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...