Этот вопрос похож на Оптимизацию поиска диапазона IP-адресов? но этот ограничен SQL Server 2000.
Предположим, у меня есть 10 миллионов диапазонов, предварительно сохраненных в таблице, структурированной и заполненной, как показано ниже.
CREATE TABLE MyTable
(
Id INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo INT NOT NULL,
CHECK (RangeTo > RangeFrom),
INDEX IX1 (RangeFrom,RangeTo),
INDEX IX2 (RangeTo,RangeFrom)
);
WITH RandomNumbers
AS (SELECT TOP 10000000 ABS(CRYPT_GEN_RANDOM(4)%100000000) AS Num
FROM sys.all_objects o1,
sys.all_objects o2,
sys.all_objects o3,
sys.all_objects o4)
INSERT INTO MyTable
(RangeFrom,
RangeTo)
SELECT Num,
Num + 1 + CRYPT_GEN_RANDOM(1)
FROM RandomNumbers
Мне нужно знать все диапазоны, содержащие значение 50,000,000
. Я пытаюсь следующий запрос
SELECT *
FROM MyTable
WHERE 50000000 BETWEEN RangeFrom AND RangeTo
SQL Server показывает, что было выполнено 10 951 логическое чтение и было прочитано около 5 миллионов строк, чтобы вернуть 12 подходящих.
Могу ли я улучшить эту производительность? Любая реструктуризация таблицы или дополнительных индексов в порядке.
sql-server
optimization
Мартин Смит
источник
источник
contains
запросы и, хотя они хорошо работают, сокращают объем чтения данных, который, по-видимому, добавляет другие. накладные расходы, которые противодействуют этому.Ответы:
Columnstore очень полезен по сравнению с некластеризованным индексом, который сканирует половину таблицы. Некластеризованный индекс columnstore обеспечивает большую часть преимуществ, но вставка упорядоченных данных в кластеризованный индекс columnstore еще лучше.
По своей структуре я могу получить исключение группы строк в
RangeFrom
столбце, что исключит половину моих групп строк. Но из-за характера данных я также получаю исключение группы строк вRangeTo
столбце:Для больших таблиц с более переменными данными существуют разные способы загрузки данных, чтобы гарантировать наилучшее исключение группы строк в обоих столбцах. В частности, для ваших данных запрос занимает 1 мс.
источник
Пол Уайт указал на ответ на аналогичный вопрос, содержащий ссылку на интересную статью Ицик Бен Ган . Здесь описывается модель «Статического дерева реляционных интервалов», которая позволяет сделать это эффективно.
Таким образом, этот подход включает в себя сохранение вычисленного значения ("forknode") на основе значений интервалов в строке. При поиске диапазонов, которые пересекают другой диапазон, можно предварительно рассчитать возможные значения узлов узла, которые должны иметь совпадающие строки, и использовать это, чтобы найти результаты максимум с 31 операцией поиска (ниже приведены целые числа в диапазоне от 0 до макс. Со знаком 32). немного int)
Исходя из этого, я реструктурировал таблицу, как показано ниже.
И затем использовал следующий запрос (статья ищет пересекающиеся интервалы, поэтому поиск интервала, содержащего точку, является вырожденным случаем этого)
Обычно это выполняется
1ms
на моем компьютере, когда все страницы находятся в кеше - со статистикой ввода-вывода.и планировать
NB. Источник использует многоэтапные TVF, а не рекурсивный CTE, чтобы заставить узлы объединяться, но в интересах сделать мой ответ самодостаточным, я выбрал последнее. Для производственного использования я бы, вероятно, использовал TVF.
источник
Мне удалось найти подход в режиме строк, который конкурирует с подходом N / CCI, но вам нужно кое-что знать о своих данных. Предположим , что у вас есть столбец , который содержал разницу
RangeFrom
иRangeTo
и вы индексируется его вместе сRangeFrom
:Если бы вы знали все различные значения,
DiffOfColumns
то вы могли бы выполнить поиск каждого значенияDiffOfColumns
с включенным фильтром диапазона,RangeTo
чтобы получить все соответствующие данные. Например, если мы знаем, чтоDiffOfColumns
= 2, то единственными допустимыми значениями дляRangeFrom
являются 49999998, 49999999 и 50000000. Рекурсию можно использовать для получения всех различных значений,DiffOfColumns
и она хорошо работает для вашего набора данных, поскольку их всего 256. Запрос ниже занимает около 6 мс на моей машине:Вы можете увидеть обычную рекурсивную часть вместе с поиском по индексу для каждого отдельного значения:
Недостаток этого подхода в том, что он начинает работать медленно, когда слишком много различных значений для
DiffOfColumns
. Давайте сделаем тот же тест, но используемCRYPT_GEN_RANDOM(2)
вместоCRYPT_GEN_RANDOM(1)
.Теперь тот же запрос находит 65536 строк из рекурсивной части и занимает 823 мс процессора на моем компьютере. PAGELATCH_SH ждет, и происходят другие неприятности. Я могу улучшить производительность, объединяя значения различий, чтобы держать под контролем число уникальных значений, и настраивая их на
CROSS APPLY
. Для этого набора данных я попробую 256 блоков:Один из способов избежать получения дополнительных строк (теперь я сравниваю округленное значение вместо истинного значения), используя фильтрацию
RangeTo
:Полный запрос теперь занимает 6 мс на моей машине.
источник
Один из альтернативных способов представления диапазона - это точки на линии.
Ниже приведен перенос всех данных в новую таблицу с диапазоном, представленным в виде
geometry
типа данных.Эквивалентный запрос для поиска диапазонов, содержащих значение,
50,000,000
приведен ниже.Показания для этого показывают улучшение по сравнению
10,951
с исходным запросом.Однако нет существенного улучшения по сравнению с оригиналом с точки зрения прошедшего времени . Типичные результаты выполнения составляют 250 мс против 252 мс.
План выполнения является более сложным, как показано ниже
Единственный случай, когда перезапись надежно работает лучше для меня, это холодный кеш.
Это разочаровывает в этом случае и трудно рекомендовать это переписать, но публикация отрицательных результатов также может быть полезна.
источник
Как дань уважения нашим новым повелителям роботов, я решил посмотреть, может ли какая-нибудь новая функциональность R и Python помочь нам здесь. Ответ - нет, по крайней мере, для сценариев, которые я мог заставить работать и возвращать правильные результаты. Если кто-то с лучшими знаниями придет, ну, не стесняйтесь шлепать меня. Мои ставки разумны.
Для этого я настроил виртуальную машину с 4 ядрами и 16 ГБ оперативной памяти, полагая, что этого будет достаточно для работы с набором данных ~ 200 МБ.
Давайте начнем с языка, которого нет в Бостоне!
р
Это было плохое время.
План выполнения довольно неинтересен, хотя я не знаю, почему средний оператор должен называть нас по именам.
Далее, кодирование с карандашами!
питон
Просто, когда вы думали, что это не может быть хуже, чем R:
Еще один нечестный план казни :
Хм и Хммер
Пока что я не впечатлен. Я не могу дождаться, чтобы удалить эту ВМ.
источник
DECLARE @input INT = 50000001; EXEC dbo.sp_execute_external_script @language = N'R', @script = N'OutputDataSet <- InputDataSet[which(x >= InputDataSet$RangeFrom & x <= InputDataSet$RangeTo) , ]', @parallel = 1, @input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable;', @params = N'@x INT', @x = 50000001 WITH RESULT SETS ( ( Id INT NOT NULL, RangeFrom INT NOT NULL, RangeTo INT NOT NULL ));
но да, производительность невелика. Я использую R для вещей, которые вы не можете сделать в SQL, скажем, если вы хотите что-то предсказать.Я нашел довольно хорошее решение с использованием вычисляемого столбца, однако оно подходит только для одного значения. При этом, если у вас есть волшебная ценность, может быть, этого достаточно.
Начиная с заданного вами образца, затем модифицируя таблицу:
Запрос просто становится:
Который возвращает те же результаты, что и ваш начальный запрос. С отключенными планами выполнения вот статистика (для краткости усечена):
И вот план запроса :
источник
WHERE (50000000 BETWEEN RangeFrom AND RangeTo) INCLUDE (..)
?CREATE INDEX IX1_redo ON dbo.MyTable (curtis_jackson) INCLUDE (RangeFrom, RangeTo) WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000
должно сработать. И запросSELECT * FROM MyTable WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000;
использует это - так что тогда не так уж много нужно для бедного КертисаМое решение основано на том наблюдении , что интервал имеет известную максимальную ширину W . Для данных примера это один байт или 256 целых чисел. Следовательно , для заданного значения параметра поиска P мы знаем наименьшее RangeFrom , что может быть в наборе результатов P - W . Добавление этого к предикату дает
Исходя из первоначальной настройки и запроса моего компьютера (64-битная Windows 10, 4-ядерная гиперпоточная i7, 2,8 ГГц, 16 ГБ ОЗУ), возвращается 13 строк. Этот запрос использует параллельный поиск индекса по индексу (RangeFrom, RangeTo). Пересмотренный запрос также выполняет параллельный поиск по тому же индексу.
Измерения для исходного и пересмотренного запросов
Для исходного запроса количество прочитанных строк равно количеству строк, которые меньше или равны @P. Оптимизатор запросов (QO) не имеет альтернативы, но читает их все, поскольку не может заранее определить, будут ли эти строки удовлетворять предикату. Многостолбцовый индекс в (RangeFrom, RangeTo) бесполезен при удалении строк, которые не соответствуют RangeTo, поскольку нет никакой корреляции между первым ключом индекса и вторым, который может быть применен. Например, первая строка может иметь небольшой интервал и быть исключенной, тогда как вторая строка имеет большой интервал и возвращается, или наоборот.
В одной неудачной попытке я попытался обеспечить эту достоверность через ограничение проверки:
Это не имело никакого значения.
Включив мои внешние знания о распределении данных в предикат, я могу заставить QO пропускать низкочастотные строки RangeFrom, которые никогда не могут быть частью результирующего набора, и проходить по ведущему столбцу индекса до допустимых строк. Это показано в разных предикатах поиска для каждого запроса.
В зеркальном аргумента верхний предел RangeTo является P + W . Однако это бесполезно, потому что нет никакой корреляции между RangeFrom и RangeTo, которая позволила бы конечному столбцу многоколоночного индекса удалять строки. Следовательно, нет смысла добавлять этот пункт в запрос.
Этот подход получает большую часть своей выгоды от небольшого размера интервала. При увеличении возможного размера интервала количество пропущенных строк с низким значением уменьшается, хотя некоторые все равно будут пропущены. В предельном случае, с интервалом, равным диапазону данных, этот подход не хуже оригинального запроса (что, как я признаю, является холодным утешением).
Я прошу прощения за любые ошибки, которые могут возникнуть в этом ответе.
источник