Могу ли я оптимизировать запрос SELECT DISTINCT x FROM largeTable, создав индекс по столбцу x? - PullRequest
22 голосов
/ 12 мая 2011

У меня огромная таблица с гораздо меньшим числом (на порядки) различных значений в некотором столбце 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.

Ответы [ 7 ]

22 голосов
/ 12 мая 2011

Вероятно, это не проблема индексации, а проблема проектирования данных.Нормализация, если быть точным.Тот факт, что вам нужно запрашивать отдельные значения поля и даже желать добавить индекс, является сильным показателем того, что поле следует нормализовать в отдельную таблицу с (небольшим) ключом соединения.Тогда отдельные значения будут сразу же доступны при сканировании намного меньшей внешней таблицы поиска.

Обновление
В качестве обходного пути вы можете создать индексированное представление для агрегата с помощью "отдельного"поле.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);
6 голосов
/ 12 мая 2011

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)
2 голосов
/ 12 мая 2011

Если вы знаете значения заранее и в столбце x есть индекс (или если каждое значение может быстро появиться при последовательном сканировании всей таблицы), гораздо быстрее запросить каждое из них по отдельности:

select vals.x
from [values] as vals (x)
where exists (select 1 from bigtable where bigtable.x = vals.x);

Продолжение использования exist () выполнит столько поисков по индексу, сколько будет допустимых значений.

Способ, которым вы его написали (что правильно, если значения заранее неизвестны)обработчику запросов потребуется прочитать всю таблицу и хеш-код агрегировать беспорядок, чтобы извлечь значения.(Что делает индекс бесполезным.)

1 голос
/ 24 декабря 2016

Нет.Но есть некоторые обходные пути (исключая нормализацию):

Как только индекс будет создан, можно реализовать в SQL то, что оптимизатор может делать автоматически:

https://stackoverflow.com/a/29286754/538763(приведено несколько обходных путей)

В других ответах говорится, что вы можете нормализовать, что решит вашу проблему, но даже после того, как его нормализованный SQL Server по-прежнему предпочитает выполнять сканирование, чтобы найти max () внутри группы (групп).Обходные пути:

https://dba.stackexchange.com/questions/48848/efficiently-query-max-over-multiple-ranges?rq=1

0 голосов
/ 12 мая 2011

Согласно вашему описанию плана выполнения, я считаю, что это наилучшее возможное выполнение.

Сканирование индекса читает весь индекс как сохраненный (не в порядке индекса), HASH MATCH делает отличное.

Возможны и другие способы решения вашей проблемы. В SQL Server мне приходят в голову индексированные представления. Тем не менее, это может дать вам большой успех для записи на этом столе.

0 голосов
/ 12 мая 2011

При выполнении SELECT DISTINCT для индексированного поля сканирование индекса имеет смысл, так как при выполнении все равно приходится сканировать каждое значение в индексе для всей таблицы (при условии, что нет условия WHERE, как кажется, в случаеваш пример).

Индексы обычно оказывают большее влияние на условия WHERE, JOINS и ORDER BY.

0 голосов
/ 12 мая 2011

Возможно.Хотя это не гарантировано - это полностью зависит от запроса.

Я предлагаю прочитать эту статью Гэйл Шоу ( часть 1 и часть 2 ).

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