как структурировать индекс для group by в Sql Server
выполнение следующего простого запроса занимает очень много времени (несколько минут).
у меня есть индекс:
create index IX on [fctWMAUA] (SourceSystemKey, AsAtDateKey)
SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
FROM [fctWMAUA] (NOLOCK) AS [t0]
WHERE SourceSystemKey in (1,2,3,4,5,6,7,8,9)
GROUP BY [t0].[SourceSystemKey]
статистика выглядит следующим образом:
- логические чтения 1827978
- физический читает 1113
- читать aheads 1806459
принимая тот же самый запрос и переформатируя его следующим образом, дает мне эту статистику:
- логических чтений 36
- физические чтения 0
- читать aheads 0
для выполнения требуется 31 МС.
SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
FROM [fctWMAUA] (NOLOCK) AS [t0]
WHERE SourceSystemKey = 1
GROUP BY [t0].[SourceSystemKey]
UNION
SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
FROM [fctWMAUA] (NOLOCK) AS [t0]
WHERE SourceSystemKey = 2
GROUP BY [t0].[SourceSystemKey]
UNION
SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
FROM [fctWMAUA] (NOLOCK) AS [t0]
WHERE SourceSystemKey = 3
GROUP BY [t0].[SourceSystemKey]
/* AND SO ON TO 9 */
как сделать индекс, который делает группу быстро?
6 ответов
попробуйте сказать SQL Server использовать индекс:
...
FROM [fctWMAUA] (NOLOCK, INDEX(IX)) AS [t0]
...
убедитесь, что статистика для таблицы актуальна:
UPDATE STATISTICS [fctWMAUA]
для получения лучших ответов включите showplan для обоих запросов:
SET SHOWPLAN_TEXT ON
и добавьте результаты к вашему вопросу.
вы также можете написать запрос без группы BY. Например, можно использовать эксклюзивное левое соединение, исключающее строки со старыми датами:
select cur.SourceSystemKey, cur.date
from fctWMAUA cur
left join fctWMAUA next
on next.SourceSystemKey = next.SourceSystemKey
and next.date > cur.date
where next.SourceSystemKey is null
and cur.SourceSystemKey in (1,2,3,4,5,6,7,8,9)
это может быть удивительно быстро, но я не думаю, что это может победить профсоюз.
Я обнаружил, что лучшим решением будет следующее. Он имитирует объединенную версию запроса и выполняется очень быстро.
40 логических считываний и время выполнения 3 мс.
SELECT [t3].[value]
FROM [dimSourceSystem] AS [t0]
OUTER APPLY (
SELECT MAX([t2].[value]) AS [value]
FROM (
SELECT [t1].[AsAtDateKey] AS [value], [t1].[SourceSystemKey]
FROM [fctWMAUA] AS [t1]
) AS [t2]
WHERE [t2].[SourceSystemKey] = ([t0].[SourceSystemKey])
) AS [t3]
его трудно сказать, не глядя на план выполнения, однако вы можете попробовать следующее:
SELECT * FROM
(
SELECT MAX(t0.AsAtDateKey) AS [Date], t0.SourceSystemKey AS SourceSystem
FROM fctWMAUA (NOLOCK) AS t0
GROUP BY t0.SourceSystemKey
)
WHERE SourceSystem in (1,2,3,4,5,6,7,8,9)
трудно сказать, не глядя на план выполнения, но я думаю, что происходит то, что SQL server недостаточно умен, чтобы понять, что указанное предложение WHERE отфильтровывает группы и не оказывает никакого влияния на записи, включенные для каждой группы. Как только SQL server осознает это, он может использовать более интеллектуальный индекс поиск максимальных значений (что и происходит во втором запросе)
просто теория, но, возможно, стоит попробовать.
используйте HAVING вместо WHERE, чтобы фильтрация происходила после группировки:
SELECT MAX(AsAtDateKey) AS [Date], SourceSystemKey AS SourceSystem
FROM fctWMAUA (NOLOCK)
GROUP BY SourceSystemKey
HAVING SourceSystemKey in (1,2,3,4,5,6,7,8,9)
Я также не особенно забочусь о предложении IN, особенно когда его можно заменить на "
WHERE SourceSystemKey = 3
GROUP BY [t0].[SourceSystemKey]
вам не нужно группировать по фиксированному полю.
любом случае я предпочитаю первое предложение. Может быть, я заменю
WHERE SourceSystemKey in (1,2,3,4,5,6,7,8,9)
Для что-то вроде
WHERE SourceSystemKey BETWEEN 1 AND 9
или
WHERE SourceSystemKey >= 1 AND SourceSystemKey <= 9
если SourceSystemKey является целым числом. Но я не думаю, что это вызовет большие перемены.
сначала я проверю статистику перестроения и перестрою все индексы для таблицы и подожду некоторое время. Восстановление не является мгновенным, это будет зависеть от того, как занят сервер, но это предложение хорошо структурировано для индекса, используемого оптимизатором.
С уважением.
вы пробовали создать другой индекс только в столбце SourceSystemKey? Большое количество логических чтений при использовании этого столбца в предложении where заставляет меня думать, что он выполняет сканирование индекса/таблицы. Не могли бы вы запустить план выполнения и посмотреть, так ли это? План выполнения может также предложить индексное предложение.