Пол Уайт указал на ответ на аналогичный вопрос, содержащий ссылку на интересную статью Ицик Бен Ган . Здесь описывается модель «Статического дерева реляционных интервалов», которая позволяет сделать это эффективно.
Таким образом, этот подход включает в себя сохранение вычисленного значения ("forknode") на основе значений интервалов в строке. При поиске диапазонов, которые пересекают другой диапазон, можно предварительно рассчитать возможные значения узлов узла, которые должны иметь совпадающие строки, и использовать это, чтобы найти результаты максимум с 31 операцией поиска (ниже приведены целые числа в диапазоне от 0 до макс. Со знаком 32). немного int)
Исходя из этого, я реструктурировал таблицу, как показано ниже.
CREATE TABLE dbo.MyTable3
(
Id INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo INT NOT NULL,
node AS RangeTo - RangeTo % POWER(2, FLOOR(LOG((RangeFrom - 1) ^ RangeTo, 2))) PERSISTED NOT NULL,
CHECK (RangeTo > RangeFrom)
);
CREATE INDEX ix1 ON dbo.MyTable3 (node, RangeFrom) INCLUDE (RangeTo);
CREATE INDEX ix2 ON dbo.MyTable3 (node, RangeTo) INCLUDE (RangeFrom);
SET IDENTITY_INSERT MyTable3 ON
INSERT INTO MyTable3
(Id,
RangeFrom,
RangeTo)
SELECT Id,
RangeFrom,
RangeTo
FROM MyTable
SET IDENTITY_INSERT MyTable3 OFF
И затем использовал следующий запрос (статья ищет пересекающиеся интервалы, поэтому поиск интервала, содержащего точку, является вырожденным случаем этого)
DECLARE @value INT = 50000000;
;WITH N AS
(
SELECT 30 AS Level,
CASE WHEN @value > POWER(2,30) THEN POWER(2,30) END AS selected_left_node,
CASE WHEN @value < POWER(2,30) THEN POWER(2,30) END AS selected_right_node,
(SIGN(@value - POWER(2,30)) * POWER(2,29)) + POWER(2,30) AS node
UNION ALL
SELECT N.Level-1,
CASE WHEN @value > node THEN node END AS selected_left_node,
CASE WHEN @value < node THEN node END AS selected_right_node,
(SIGN(@value - node) * POWER(2,N.Level-2)) + node AS node
FROM N
WHERE N.Level > 0
)
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
JOIN N AS L
ON I.node = L.selected_left_node
AND I.RangeTo >= @value
AND L.selected_left_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
JOIN N AS R
ON I.node = R.selected_right_node
AND I.RangeFrom <= @value
AND R.selected_right_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
WHERE node = @value;
Обычно это выполняется 1ms
на моем компьютере, когда все страницы находятся в кеше - со статистикой ввода-вывода.
Table 'MyTable3'. Scan count 24, logical reads 72, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 4, logical reads 374, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
и планировать
NB. Источник использует многоэтапные TVF, а не рекурсивный CTE, чтобы заставить узлы объединяться, но в интересах сделать мой ответ самодостаточным, я выбрал последнее. Для производственного использования я бы, вероятно, использовал TVF.