У меня есть таблица SQL Server с более чем 3 миллиардов строк. Один из моих запросов занимает очень много времени, поэтому я рассматриваю возможность его оптимизации. Запрос выглядит так:
SELECT [Enroll_Date]
,Count(*) AS [Record #]
,Count(Distinct UserID) AS [User #]
FROM UserTable
GROUP BY [Enroll_Date]
[Enroll_Date] - это столбец с низкой селективностью, содержащий менее 50 возможных значений, а столбец UserID - это столбец с высокой селективностью, содержащий более 200 миллионов различных значений. Основываясь на моих исследованиях, я считаю, что я должен создать некластеризованный составной индекс для этих двух столбцов, и теоретически столбец высокой селективности должен быть первым столбцом. Но я не уверен, что в моем случае это сработает, потому что я использую столбец низкой селективности в предложении group by.
Эта таблица не имеет кластеризованного индекса.
источник
Ответы:
В качестве альтернативы решению @ AaronBertrand (если вы не можете или не хотите создавать индексированное представление), я бы порекомендовал вам создать индекс
(Enroll_Date, UserID)
. Если этот тип вопросов очень распространен в вашей таблице, это, вероятно, даже должен быть ваш кластерный индекс.Я бы не стал рекомендовать индексы высокой селективности в качестве общей «наилучшей практики», а скорее посмотрю, какой индекс даст вашему запросу наилучшую производительность.
Индекс на
(Enroll_Date, UserID)
даст вашему запросу высоко оптимизированный, неблокирующий план запроса с агрегатами потоков.«Неблокирующая» в этом контексте означает, что запросу не нужно буферизовать какие-либо значительные объемы данных (как, например, сортировка или агрегат хэшей), что означает, что он (а) немедленно начинает возвращать строки, и ( б) практически не потребляет рабочую память.
источник
Ответ Аарона - отличное решение. Я отвечу на вопрос, если вы не хотите использовать этот подход.
Запрос, который вы разместили, будет обычно выполняться сначала в группе
(Enroll_Date, UserID)
, а затем снова(Enroll_Date)
. Эта оптимизация является новой для SQL Server 2012. Она вступает в силу в случае одногоCOUNT DISTINCT
.Индекса для этих двух столбцов в определенном порядке
(Enroll_Date, UserID)
будет достаточно для получения эффективного плана, который объединяет сканирование индекса в два последовательных потоковых агрегата. Противоположный порядок не позволил бы этот план.Поэтому используйте порядок
(Enroll_Date, UserID)
. У вас нет выбора здесь.источник
Походит на идеальный сценарий для индексированного представления, которое позволяет вам платить за вычисления и агрегаты во время записи вместо времени запроса.
Это займет некоторое время для создания и, конечно, потребует сопровождения во всех операциях DML, точно так же, как индекс в базовой таблице.
Теперь запрос к этому представлению будет очень похожим - каждая строка в представлении теперь представляет отдельную комбинацию пользователя / даты, так что цифра может быть вычислена по одному COUNT (*), тогда как общее количество строк в базовой таблице равно уже частично агрегированы для вас, теперь вам просто нужно добавить их, используя SUM на дату:
Добавлена подсказка NOEXPAND, после запоминания этого и этого .
Я могу безоговорочно сказать вам, что этот запрос будет быстрее, чем ваш текущий запрос (но не на сколько), за исключением редкого случая, когда у вас есть ровно один пользователь на каждую дату (в этом случае тот же объем данных будет иметь для чтения), и столбцы, о которых мы знаем, являются единственными столбцами в индексе базовой таблицы. О том, стоит ли повышение производительности во время чтения дополнительной работы, которая повлияет на часть записи вашей рабочей нагрузки, мы не можем вам сказать - вам придется протестировать ее, чтобы измерить компромисс (никакой индекс не является бесплатным).
И если вы часто используете одни и те же общие предложения WHERE для Enroll_Date для конкретных, четко определенных диапазонов (скажем, текущего квартала или года до даты), вы можете добавить соответствующие отфильтрованные индексы, которые еще больше уменьшат этот ввод / вывод (но всегда есть компромисс).
Вы можете также рассмотреть возможность размещения кластеризованного индекса на базовой таблице. Похоже, это не один из тех очень редких вариантов использования, которые выигрывают от кучи.
источник