Какие индексы оптимизируют этот запрос с четырьмя объединениями? - PullRequest
4 голосов
/ 12 мая 2009

У меня есть SQL-запрос с внутренними объединениями четырех таблиц, который занимает более 30 секунд с текущими индексами и структурой запроса. Я хотел бы сделать это как можно быстрее; как минимум быстрее 5 секунд.

Сначала я подумал о денормализации, но прочитал здесь , что, как правило, должна быть возможность оптимизации с помощью правильных индексов и т. Д. Я не могу понять это в этом случае. Текущий план запроса содержит сканирование индекса для самой маленькой таблицы и предупреждение «Нет предиката соединения» для одного из внутренних объединений.

  • Как я могу оптимизировать скорость следующего?
  • Какие показатели?
  • Какая структура запроса?
  • Другие соображения?

У нас есть следующие таблицы (с указанием количества строк и соответствующих полей):

TableName           Rows  Fields
------------------- ----- ----------------------------------------------
ProjectType         150   ProjectTypeID, ProjectTypeName
Employee            200   EmployeeID, RefDepartmentID
Project             0.2M  ProjectID, RefProjectTypeID
ProjectTransaction  3.5M  Hours, RefEmployeeID, RefProjectID, Date, Type

Запрос должен суммировать часы для данного отдела, диапазона дат и т. Д. В настоящее время я пытаюсь:

SELECT E.RefDepartmentID, SUM(PTran.Hours)
FROM Employee E
JOIN ProjectTransaction PTran
    ON E.EmployeeID = PTran.RefEmployeeID
JOIN Project P
    ON PTran.RefProjectID = P.ProjectID
JOIN ProjectType PType
    ON P.RefProjectTypeID = PType.ProjectTypeID
WHERE E.RefDepartmentID = @departmentID
    AND @from <= PTran.Date AND PTran.Date <= @to
    AND PTran.Type = 0
    AND PType.ProjectTypeName NOT IN (N'1', N'2', N'3')
GROUP BY E.RefDepartmentID

Спасибо за все быстрые ответы. (У меня уже были индексы по «внешним ключам» и критериям в предложении WHERE.) Я переупорядочил запрос так, чтобы сначала были две маленькие таблицы, затем средняя и последняя. И вуаля занимает около одной секунды:

SELECT E.RefDepartmentID, SUM(PTran.Hours)
FROM Employee E
JOIN ProjectType PType
    ON E.RefCustomerID = PType.RefCustomerID
JOIN Project P
    ON PType.ProjectTypeID = P.RefProjectTypeID
JOIN ProjectTransaction PTran
    ON E.EmployeeID = PTran.RefEmployeeID
    AND P.ProjectID = PTran.RefProjectID
WHERE E.RefDepartmentID = @departmentID
    AND @from <= PTran.Date AND PTran.Date <= @to
    AND PTran.Type = 0
    AND PType.ProjectTypeName NOT IN (N'1', N'2', N'3')
GROUP BY E.RefDepartmentID

Ответы [ 7 ]

9 голосов
/ 12 мая 2009

Это не всегда работает, но попробуйте:

  1. Изменение порядка таблиц в соединениях от самого маленького к самому большому.
  2. Использовать подзапрос вместо ProjectTransaction table:

    JOIN (ВЫБЕРИТЕ RefEmployeeID, RefProjectID ИЗ ProjectTransaction WHERE @from <= PTran.Date AND PTran.Date <= @to AND PTran.Type = 0) AS trans </p>

1 голос
/ 12 мая 2009

Индексы для всех внешних ключей, очевидно.

Кроме того, ProjectTransaction - ваша единственная действительно большая таблица, и вы выполняете много фильтрации по ней в предложении Where. Я бы добавил индексы ко всем столбцам, по которым вы фильтруете:

ProjectTransaction.Date
ProjectTransaction.Type

edit: Поскольку у вас есть таблица с 3,5 миллионами строк, еще один метод оптимизации, который вы можете рассмотреть, - это обновление оборудования на вашем SQL Server. Некоторая дополнительная память или несколько дополнительных процессоров иногда могут быть более экономически эффективными, чем время, затрачиваемое на оптимизацию (очевидно, в зависимости от затрат на разработку и того, сколько вы проводите оптимизации) ... см. Оборудование дешевое, программисты дорогие .

1 голос
/ 12 мая 2009

Предполагая, что P.ProjectID и PType.ProjectTypeID являются PRIMARY KEY, создайте этот индекс:

CREATE INDEX ix_ptran_emloyee_type_date ON ProjectTransaction(RefEmployeeID, Type, Date)

и избавьтесь от GROUP BY:

SELECT  @departmentID, SUM(PTran.Hours)
FROM    Employee E
JOIN    ProjectTransaction PTran
ON      PTran.RefEmployeeID = E.EmployeeID
JOIN    Project P
ON      P.ProjectID = PTran.RefProjectID
JOIN    ProjectType PType
ON      PType.ProjectTypeID = P.RefProjectTypeID
WHERE   E.RefDepartmentID = @departmentID
        AND PTran.Date BETWEEN @from AND @to
        AND PTran.Type = 0
        AND PType.ProjectTypeName NOT IN (N'1', N'2', N'3')
1 голос
/ 12 мая 2009

Убедитесь, что у вас есть индексы для столбцов внешнего ключа.

0 голосов
/ 12 мая 2009

До сих пор все советы были правильными, но если вам нужно мнение эксперта (то есть SQL Server), вы могли бы сделать хуже, чем использовать Помощник по настройке ядра СУБД . Даже если у вас нет обширных реальных данных, советник может сделать несколько разумных предложений на основе небольшого набора тестовых данных.

0 голосов
/ 12 мая 2009

Советник по настройке ядра СУБД 1002 * сообщит вам, если вы просто спросите его.

0 голосов
/ 12 мая 2009

Что говорит ваш план запроса? Если у вас есть два сканирования индекса в одной и той же таблице с последующим хеш-соединением (или аналогичным) между результатами обоих, создайте составной индекс с полями из обоих сканирований индекса - тогда БД может выполнить один поиск индекса в составном указателе.

Убедитесь, что у вас есть актуальная статистика. База данных может лучше оптимизироваться, если таковые имеются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...