как структурировать индекс для 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 заставляет меня думать, что он выполняет сканирование индекса/таблицы. Не могли бы вы запустить план выполнения и посмотреть, так ли это? План выполнения может также предложить индексное предложение.