как структурировать индекс для группы в 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
  • читать вперед 1806459

Взяв тот же самый запрос и переформатировав его следующим образом, я получаю следующую статистику:

  • логические чтения 36
  • физическое чтение 0
  • читать вперед 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 */

Как мне сделать индекс, который быстро выполняет группу?


person Craig    schedule 04.11.2009    source источник
comment
У вас есть индекс SourceSystemKey? Если нет, я думаю, вы можете вызвать полное сканирование таблицы.   -  person heferav    schedule 04.11.2009
comment
Что показывает шоу-план? и какие значения может принимать SourceSystemKey?   -  person mmmmmm    schedule 04.11.2009


Ответы (6)


Попробуйте указать SQL Server использовать индекс:

...
FROM [fctWMAUA] (NOLOCK, INDEX(IX)) AS [t0]
...

Убедитесь, что статистика для таблицы актуальна:

UPDATE STATISTICS [fctWMAUA]

Чтобы получить более точные ответы, включите план показа для обоих запросов:

SET SHOWPLAN_TEXT ON

и добавьте результаты к вашему вопросу.

Вы также можете написать запрос без GROUP BY. Например, вы можете использовать эксклюзивное LEFT JOIN, исключая строки с более старыми датами:

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)

Это может быть на удивление быстро, но я не думаю, что это сможет превзойти UNION.

person Andomar    schedule 04.11.2009
comment
Перепробовал все ваши предложения. Все еще очень медленно. Юнион по-прежнему быстр. ‹pre› |--Агрегат потоков(GROUP BY:([t0].[SourceSystemKey]) DEFINE:([Expr1003]=MAX([partialagg1004]))) |--Parallelism(Gather Streams, ORDER BY:([t0 ].[SourceSystemKey] ASC)) |--Stream Aggregate(GROUP BY:([t0].[SourceSystemKey]) DEFINE:([partialagg1004]=MAX([KITE].[dbo].[fctWMAUA].[AsAtDateKey] as [t0].[AsAtDateKey]))) |--Поиск по индексу(ОБЪЕКТ:([KITE].[dbo].[fctWMAUA].[IX_AsAtDateSourceSystem] AS [t0]), SEEK:([t0].[SourceSystemKey ] ›= (1) AND [t0].[SourceSystemKey] ‹= (9)) ORDERED FORWARD) ‹/pre› - person Craig; 04.11.2009
comment
Я также переупорядочиваю поля в индексе, и он не меняется. - person Craig; 04.11.2009
comment
Глядя на план, это вроде как имеет смысл. Этот первоначальный поиск найдет все записи. Есть только девять исходных систем, и он ищет много. - person Craig; 04.11.2009
comment
Имеет ли значение добавление OPTION (HASH GROUP) или OPTION (ORDER GROUP) в конце запроса? - person Andomar; 04.11.2009
comment
Привет Андомар, Хорошее предложение. Хэш-группа сократила время примерно до пятнадцати секунд, что приемлемо, если я кеширую результаты. Все еще странно, что я могу получить 32 мс от версии объединения и ничего близкого от группы по версии. Версия объединения выполняет поиск и вершину (1) для каждого из запросов, что очень быстро. Кажется, индекс не может дублировать это. - person Craig; 05.11.2009
comment
Я реструктурировал запрос, чтобы запросить исходные системы, а затем выполнить внутренний запрос для каждой из максимальных дат. Это отлично использует индекс и занимает около 7 мс. Намного быстрее, чем group by, а также быстрее, чем union. выберите SourceSystems.SourceSystemKey (выберите max(AsAtDateKey) из fctWMAUA, где fctWMAUA.SourceSystemKey = группа SourceSystems.SourceSystemKey с помощью fctWMAUA.SourceSystemKey) MaxData из SourceSystems - person Craig; 09.11.2009

Трудно сказать, не глядя на план выполнения, однако вы можете попробовать следующее:

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-сервер недостаточно умен, чтобы понять, что указанное предложение WHERE отфильтровывает группы и не оказывает никакого влияния на записи, включенные для каждого группа. Как только SQL-сервер понимает это, он может использовать более интеллектуальный поиск по индексу для определения максимальных значений (что и происходит во втором запросе).

Это всего лишь теория, но, возможно, стоит попробовать.

person Justin    schedule 23.11.2009

Я обнаружил, что лучшим решением является следующее. Он имитирует объединенную версию запроса и выполняется очень быстро.

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]
person Craig    schedule 30.11.2009

Используйте 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, особенно когда его можно заменить на «‹10» или «Между 1 и 9», которые лучше используются в отсортированных индексах.

person BradC    schedule 23.11.2009

 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

or

 WHERE SourceSystemKey >= 1 AND SourceSystemKey <= 9

если SourceSystemKey является целым числом. Но я не думаю, что это вызовет большие изменения.

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

С Уважением.

person j.a.estevan    schedule 04.11.2009
comment
Что вы имеете в виду под «Не нужно группировать по фиксированному полю»? Он ищет максимальную дату. - person Andomar; 04.11.2009
comment
Я пробовал между, и это ничего не изменило. Он использует индекс, и первоначальный поиск по индексу возвращает 665 миллионов строк. Используя объединение, он ищет одну строку (верхнюю 1) для каждого правильно заказанного максимума, и это очень быстро. Без объединения он ищет 665 миллионов строк и выполняет итерацию. Это безумие. Оба запроса определенно используют одни и те же индексы в плане. - person Craig; 04.11.2009
comment
Андомар: Я говорил о GROUP BY, потому что если поставить WHERE SourceSystemKey = 3, я не вижу смысла в GROUP BY SourceSystemKey, потому что есть только SourceSystemKey. Группировать нечего, вы ищете абсолютное значение MAX, которое проходит фильтр WHERE. Но в любом случае оптимизатор знает об этом и не должен быть проблемой. (редактировать: речь о второй команде. В первом случае GROUP BY, очевидно, в порядке) - person j.a.estevan; 04.11.2009
comment
@ j.a.estevan: SQL Server требует GROUP BY, прежде чем он позволит вам использовать агрегаты, такие как MAX () - person Andomar; 04.11.2009
comment
В данном сценарии это не требуется. Как правило, это не требуется, если вам не нужно группировать данные. Просто попробуйте (например): выберите max(object_id) из sys.tables, где имя типа «%A%». Это отлично работает в SQL Server 2005. - person j.a.estevan; 04.11.2009
comment
Вы правы, группа по не требуется в объединенной версии запроса. Я копировал/вставлял запрос. Однако это не влияет на план запроса. - person Craig; 05.11.2009
comment
@j.a.estevan: Спасибо, это избавит меня от необходимости печатать! - person Andomar; 05.11.2009

Вы пытались создать другой индекс только для столбца SourceSystemKey? Большое количество логических чтений, когда вы используете этот столбец в предложении where, заставляет меня думать, что он выполняет сканирование индекса/таблицы. Не могли бы вы запустить план выполнения и посмотреть, так ли это? План выполнения также может содержать предложение по индексу.

person Nir    schedule 19.11.2009