Замедление в индексированном представлении для SQL 2005 - PullRequest
6 голосов
/ 10 марта 2010

Допустим, у меня есть очень длинная таблица (~ 35 миллионов строк), которая называется TimeCard и имеет всего 5 столбцов (tableID, CompanyID, UserID, ProjectID, DailyHoursWorked, entryDate). Это довольно простая таблица, в которой записываются рабочие часы сотрудников в день на проект для каждой компании.

Теперь мне нужно сгенерировать отчет, чтобы узнать общее количество рабочих часов сотрудников в месяц на проект для любой конкретной компании. Вместо того чтобы выполнять агрегацию, необходимую при выполнении отчета, я хочу создать табличную структуру данных, в которой уже собраны все данные о компании / проекте / пользователе, сгруппированные по месяцам, поэтому при выполнении отчета я могу просто запросить эту структуру данных напрямую без выполнения агрегации во время выполнения, поскольку ~ 35 миллионов записей могут занять несколько минут.

Так что у меня есть 2 разных способа. Создайте дополнительную физическую таблицу с (CompanyID, UserID, ProjectID, MonthlyHoursWorked, Month) в качестве моих столбцов и просто используйте триггер в таблице TimeCard для изменения значений в дополнительной таблице. Или я могу создать индексированное представление. Поэтому я попробовал оба. Сначала я попробовал индексированное представление с помощью следующего кода:

CREATE VIEW [dbo].[vw_myView] WITH SCHEMABINDING AS
SELECT 
 JobID,
 ProjectID,
 Sum(DailyHoursWorked) AS MonthTotal,
 DATEADD( Month, DATEDIFF( Month, 0, entryDate), 0 ) AS entryMonth,
 CompanyID,
 COUNT_BIG(*) AS Counter
FROM
 dbo.TimeCard 
Group By DATEADD( Month, DATEDIFF( Month, 0, entryDate ), 0 ), JobID, ProjectID, CompanyID

Go
CREATE UNIQUE CLUSTERED INDEX [IX_someIndex] ON [dbo].[vw_myView] 
(
 [CompanyID] ASC,
 [entryMonth] ASC,
 [UserID] ASC,
 [ProjectID] ASC
)

Индексированное представление создано правильно и содержит в общей сложности ~ 5 миллионов строк.

Однако каждый раз, когда я очищаю кэш SQL и выполняю следующий запрос: * select * from vw_myView, где companyID = 1 *, это занимает почти 3 минуты. Если я выберу дополнительный маршрут таблицы, как я уже упоминал выше, с очищенным кэшем, это займет около 4 секунд.

Мои вопросы: является ли индексированный просмотр плохим выбором для данного конкретного сценария? В частности, мне интересно знать, будет ли пересчитываться / агрегироваться все индексированное представление каждый раз, когда базовая таблица (TimeCard) изменяется или когда к ней выполняется запрос?

Спасибо!

Ответы [ 6 ]

2 голосов
/ 10 марта 2010

Если вы не используете версию Enterprise или Developer , то вам нужно использовать подсказку with (noexpand):

select * 
from vw_myView with (noexpand)
where companyID = 1

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

A совет от Microsoft :

Как общая рекомендация, любой модификации или обновления для просмотра или базовые таблицы, лежащие в основе представления следует выполнять партиями, если возможно, а не синглтон операции. Это может уменьшить некоторые накладные расходы в виде обслуживания.

1 голос
/ 10 марта 2010

Я думаю, что вы на правильном пути, используя индекс View. Тем не менее, если вы поместили индексы в таблицу, из которой вы запрашиваете, TimeCard для ваших агрегированных столбцов. Вам нужно сделать индекс JobID, ProjectID, entryDate, CompanyID (1 индекс). Если вы используете 1 индекс для каждого столбца, это НЕ решит ваши проблемы, потому что Запрос должен использовать все 4 индекса вместе.

Я думаю, что использование триггера будет медленным, но другим способом. Это ускорит ваш запрос, но замедлит каждую вставку в TimeCard. Если вы решите воспользоваться триггером, я бы позаботился о том, чтобы индексировать и эту таблицу, или она может быть медленной, не медленной на 3 минуты, но все же медленной для сортировки и возврата данных.

0 голосов
/ 10 марта 2010

Что ж, идея индексированного представления определенно хороша, и если вы можете создать кластеризованный индекс для него - идеально. Это должно быть быстро - намного лучше, чем 3 минуты на запрос!

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

Я бы не рекомендовал использовать триггеры для постоянного обновления такой таблицы фактов - если вам действительно необходимо иметь самые свежие данные в каждую секунду в течение дня, то оставайтесь с индексированное представление.

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

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

Может быть, это не то решение, которое вы ищете, но просто подумайте об этом немного. Это может - или не может - сработать для вас!

0 голосов
/ 10 марта 2010

Рассматривали ли вы разбиение таблицы. Вы можете подумать о комбинации списка и таблицы хэширования.

0 голосов
/ 10 марта 2010

Не думаю, что вам нужно индексированное представление (я не говорю, что индексированное представление - плохая / хорошая идея). Я думаю, вам нужен индекс по столбцам "CompanyID" и "EntryDate". После этого вы должны использовать условие условия "WHERE CompanyID = @CompanyID AND EntryDate> = @StartDate AND EntryDate <= @EndDate". </p>

Если таблица обрабатывается в основном «EntryDate», вы можете использовать кластерный индекс для столбца «EntryDate».

После этого, я думаю, оператор select будет намного быстрее, чем сейчас.

0 голосов
/ 10 марта 2010

Я бы не использовал представление для этого. Я думаю, что таблица, заполненная триггером, - это путь. Но не забудьте откорректировать итоги обновлений и удалений, а также вставок.

...