Могу ли я оптимизировать запрос SELECT DISTINCT x FROM largeTable, создав индекс в столбце x?
У меня есть огромная таблица, имеющая гораздо меньшее количество (по порядку величины) различных значений в некотором столбце x
.
Мне нужно сделать запрос типа SELECT DISTINCT x FROM hugeTable
, и я хочу сделать это относительно быстро.
Я сделал что-то вроде CREATE INDEX hugeTable_by_x ON hugeTable(x)
, но по какой-то причине, хотя вывод мал, выполнение запроса происходит не так быстро. План запроса показывает, что 97% времени тратится на сканирование индексов hugeTable_by_x
, при этом количество строк равно размеру всей таблицы. Далее следует, в частности, операция Hash Match.
Поскольку я создал индекс в столбце x
, не могу ли я ожидать, что этот запрос будет работать очень быстро?
Обратите внимание, что я использую Microsoft SQL Server 2005.
Ответы
Ответ 1
Это, скорее всего, не проблема индексирования, а одно из данных. Нормализация, если быть точным. Тот факт, что вам нужно запрашивать различные значения поля и даже желать добавить индекс, является сильным индикатором того, что поле должно быть нормализовано в отдельную таблицу с (маленьким) ключом соединения. Затем различные значения будут доступны сразу, сканируя гораздо меньшую внешнюю таблицу поиска.
Update
В качестве обходного пути вы можете создать индексированное представление по совокупности в поле 'different'. COUNT_BIG
- это совокупность, разрешенная в индексированных представлениях:
create view vwDistinct
with schemabinding
as select x, count_big(*)
from schema.hugetable
group by x;
create clustered index cdxDistinct on vwDistinct(x);
select x from vwDistinct with (noexpand);
Ответ 2
SQL Server не реализует никаких средств для прямого поиска следующего отдельного значения в индексе, пропускающего дубликаты по пути.
Если у вас много дубликатов, вы можете использовать рекурсивный CTE для имитации этого. Техника происходит от здесь. ("Сверхбыстрый DISTINCT с использованием рекурсивного CTE"). Например:
with recursivecte as (
select min(t.x) as x
from hugetable t
union all
select ranked.x
from (
select t.x,
row_number() over (order by t.x) as rnk
from hugetable t
join recursivecte r
on r.x < t.x
) ranked
where ranked.rnk = 1
)
select *
from recursivecte
option (maxrecursion 0)
Ответ 3
Если вы знаете значения заранее и есть индекс в столбце x (или если каждое значение, скорее всего, появится быстро при сканировании seq всей таблицы), гораздо быстрее запросить каждый отдельно:
select vals.x
from [values] as vals (x)
where exists (select 1 from bigtable where bigtable.x = vals.x);
При использовании функции exists() будет выполняться столько запросов индекса, сколько допустимых значений.
Как вы его написали (это правильно, если значения неизвестны заранее), механизм запроса должен будет прочитать всю таблицу, а хэш - заполнить беспорядок, чтобы извлечь значения. (Что делает индекс бесполезным.)
Ответ 4
Нет. Но есть некоторые обходные пути (исключая нормализацию):
Как только индекс окажется на месте, тогда его можно реализовать в SQL, что оптимизатор может делать автоматически:
fooobar.com/questions/302300/... (упоминаются многочисленные обходные пути)
Другие ответы говорят, что вы можете нормализовать, что бы решить вашу проблему, но даже когда нормализованный SQL Server по-прежнему любит выполнять сканирование, чтобы найти max() внутри группы (групп). Обходные:
https://dba.stackexchange.com/questions/48848/efficiently-query-max-over-multiple-ranges?rq=1
Ответ 5
При выполнении SELECT DISTINCT
в индексированном поле сканирование индекса имеет смысл, поскольку выполнение все равно должно проверять каждое значение в индексе для всей таблицы (при условии отсутствия предложения WHERE
, как это имеет место ваш пример).
Индексы обычно оказывают большее влияние на условия WHERE
, JOINS
и ORDER BY
.
Ответ 6
Согласно вашему описанию плана выполнения, я считаю, что это наилучшее выполнение.
Index Scan считывает весь индекс как хранящийся (не в индексном порядке), HASH MATCH делает отдельный.
Могут быть другие способы решения вашей проблемы. В SQL Server индексированные представления приходят мне в голову. Однако это может дать вам большой успех для записи на эту таблицу.
Ответ 7
Возможно. Хотя это не гарантировано - это полностью зависит от запроса.
Я предлагаю прочитать эту статью Гейл Шоу (часть 1 и часть 2).