Почему мой SQL Server ORDER BY работает медленно, несмотря на индексированный упорядоченный столбец? - PullRequest
16 голосов
/ 03 июня 2011

У меня есть SQL-запрос (сгенерированный LINQ to Entities), который примерно такой:

SELECT * FROM [mydb].[dbo].[employees]
JOIN [mydb].[dbo].[industry]
  ON jobs.industryId = industry.id
JOIN [mydb].[dbo].[state]
  ON jobs.stateId = state.id
JOIN [mydb].[dbo].[positionType]
  ON jobs.positionTypeId = positionType.id
JOIN [mydb].[dbo].[payPer]
  ON jobs.salaryPerId = payPer.id
JOIN [mydb].[dbo].[country]
  ON jobs.countryId = country.id
WHERE countryName = 'US'
ORDER BY startDatetime

Запрос возвращает около 1200 строк, что я не думаю, что это огромное количество. К сожалению, это также занимает ~ 16 секунд. Без ORDER BY запрос занимает <1 секунду. </p>

Я использовал SQL Server Management Studio для размещения индекса в столбце startDatetime, а также кластерного индекса для «cityId, industryId, startDatetime, positionTypeId, payPerId, stateId» (т. Е. Всех столбцов в «jobs», которые мы используем в JOIN, а в столбце мы используем ORDER BY on). У меня уже есть отдельные индексы для каждого из столбцов, которые мы используем в JOIN. К сожалению, это не ускорило запрос.

Я запустил шоу-план и получил:

   |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[cityId]))
       |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[stateId]))
       |    |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[industryId]))
       |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[positionTypeId]))
       |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[salaryPerId]))
       |    |    |    |    |--Sort(ORDER BY:([mydb].[dbo].[jobs].[issueDatetime] ASC))
       |    |    |    |    |    |--Hash Match(Inner Join, HASH:([mydb].[dbo].[currency].[id])=([mydb].[dbo].[jobs].[salaryCurrencyId]))
       |    |    |    |    |         |--Index Scan(OBJECT:([mydb].[dbo].[currency].[IX_currency]))
       |    |    |    |    |         |--Nested Loops(Inner Join, WHERE:([mydb].[dbo].[jobs].[countryId]=[mydb].[dbo].[country].[id]))
       |    |    |    |    |              |--Index Seek(OBJECT:([mydb].[dbo].[country].[IX_country]), SEEK:([mydb].[dbo].[country].[countryName]='US') ORDERED FORWARD)
       |    |    |    |    |              |--Clustered Index Scan(OBJECT:([mydb].[dbo].[jobs].[PK_jobs]))
       |    |    |    |    |--Clustered Index Seek(OBJECT:([mydb].[dbo].[payPer].[PK_payPer]), SEEK:([mydb].[dbo].[payPer].[id]=[mydb].[dbo].[jobs].[salaryPerId]) ORDERED FORWARD)
       |    |    |    |--Clustered Index Seek(OBJECT:([mydb].[dbo].[positionType].[PK_positionType]), SEEK:([mydb].[dbo].[positionType].[id]=[mydb].[dbo].[jobs].[positionTypeId]) ORDERED FORWARD)
       |    |    |--Clustered Index Seek(OBJECT:([mydb].[dbo].[industry].[PK_industry]), SEEK:([mydb].[dbo].[industry].[id]=[mydb].[dbo].[jobs].[industryId]) ORDERED FORWARD)
       |    |--Clustered Index Seek(OBJECT:([mydb].[dbo].[state].[PK_state]), SEEK:([mydb].[dbo].[state].[id]=[mydb].[dbo].[jobs].[stateId]) ORDERED FORWARD)
       |--Clustered Index Seek(OBJECT:([mydb].[dbo].[city].[PK_city]), SEEK:([mydb].[dbo].[city].[id]=[mydb].[dbo].[jobs].[cityId]) ORDERED FORWARD)

Важной строкой является "| --Sort (ORDER BY: ([mydb]. [Dbo]. [Jobs]. [IssueDatetime] ASC))" & mdash; без упоминания индекса в этом столбце.

Почему мой ORDER BY делает мой запрос намного медленнее, и как я могу ускорить мой запрос?

Ответы [ 5 ]

12 голосов
/ 03 июня 2011

Если ваш запрос к тому времени не содержит порядка, он вернет данные в том порядке, в котором он был найден.Нет никакой гарантии, что данные будут даже возвращены в том же порядке при повторном запуске запроса.

Когда вы включаете предложение order by, база данных должна составлять список строк в правильном порядке.и затем верните данные в этом порядке.Это может занять много дополнительной обработки, что приводит к дополнительному времени.

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

Попробуйте вернуть меньше столбцов (укажите столбцы, которые вам нужны, а не Select *) и посмотрите, не выполнен ли запросработает быстрее.

7 голосов
/ 03 июня 2011

Поскольку ваш запрос проецирует все столбцы (*), ему требуется 5 столбцов для условий соединения и имеется неселективное предложение WHERE для того, что, вероятно, является столбцом объединенной таблицы, он вызывает попадание на Переломный момент индекса : оптимизатор решает, что дешевле отсканировать всю таблицу, отфильтровать ее и отсортировать, чтобы выполнить сканирование индекса по диапазону, а затем найти каждый ключ в таблице, чтобы получить необходимые дополнительные столбцы (5 для объединений, а остальные для *).

Лучшим индексом для частичного покрытия этого запроса может быть:

CREATE INDEX ... ON .. (countryId, startDatetime);

Предложение Джеффри сделать кластеризованный индекс покрыл бы запрос на 100% и определенно улучшит производительность, но изменив кластеризованный индексимеет много побочных эффектов.Я бы начал с некластеризованного индекса, как указано выше.Если они не нужны другим запросам, вы можете отбросить все другие созданные вами некластеризованные индексы, они не помогут в этом запросе.

3 голосов
/ 03 июня 2011

Вы должны попробовать также код ниже

Вставить записи во временную таблицу Без использования Order by clause

SELECT * into #temp FROM [mydb].[dbo].[employees]
JOIN [mydb].[dbo].[industry]
  ON jobs.industryId = industry.id
JOIN [mydb].[dbo].[state]
  ON jobs.stateId = state.id
JOIN [mydb].[dbo].[positionType]
  ON jobs.positionTypeId = positionType.id
JOIN [mydb].[dbo].[payPer]
  ON jobs.salaryPerId = payPer.id
JOIN [mydb].[dbo].[country]
  ON jobs.countryId = country.id
WHERE countryName = 'US'

Теперь запустите оператор, используя Order By Clause

Select * from #temp ORDER BY startDatetime
2 голосов
/ 03 июня 2011

В каком порядке включены поля в кластерном индексе?Сначала вы захотите поместить поле startDateTime, чтобы ORDER BY соответствовало ему, или в этом случае (countryId, startDateTime) в этом порядке, так как вы хотите выбрать один countryId (косвенно, через countryName), а затем заказать по startDateTime.

1 голос
/ 03 июня 2011

Вспышка новостей: индексирование столбца не помогает ускорить сортировку.

Если вы хотите, чтобы ваш запрос ОЧЕНЬ быстрее, измените порядок своих таблиц. В частности, перечислите таблицу country первой в ваших объединенных таблицах. Причина? Предложение where может фильтровать строки из первой таблицы, вместо того чтобы выполнять все эти объединения, затем фильтрует строки.

...