为什么我的 SQL Server ORDER BY 很慢,尽管已对有序列建立了索引?

问题描述 投票:0回答:5

我有一个 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 second.

我使用 SQL Server Management Studio 在 startDatetime 列上放置索引,并在“cityId、industryId、startDatetime、positionTypeId、payPerId、stateId”(即我们使用的“jobs”中的所有列)上放置聚集索引在 JOIN 和列上我们使用 ORDER BY)。我已经在 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))”——没有提及该列上的索引。

为什么我的 ORDER BY 让我的查询速度如此之慢,如何加快查询速度?

sql sql-server sql-server-2005 linq-to-entities
5个回答
20
投票

如果您的查询不包含订单,那么它将按照找到的顺序返回数据。甚至无法保证当您再次运行查询时数据会以相同的顺序返回。

当您包含 order by 子句时,数据库必须以正确的顺序构建行列表,然后按该顺序返回数据。这可能需要大量额外的处理,从而转化为额外的时间。

对查询可能返回的大量列进行排序可能需要更长的时间。在某些时候,您将耗尽缓冲区空间,数据库将不得不开始交换,性能将会下降。

尝试返回更少的列(指定您需要的列而不是 select *)并查看查询是否运行得更快。


7
投票

因为您的查询投影了所有列 (

*
),所以它需要 5 列用于连接条件,并且在可能连接的表列上有一个非选择性的
WHERE
子句,这会导致它达到 索引临界点 :优化器认为扫描整个表、过滤它并对其进行排序的成本较低,而范围扫描索引,然后查找表中的每个键以检索所需的额外列(5 用于连接和其余的为
*
)。

部分覆盖此查询的更好索引可能是:

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

Jeffrey建议建立聚集索引可以100%覆盖查询,肯定会提高性能,但是改变聚集索引有很多副作用。我将从上面的非聚集索引开始。除非其他查询需要它们,否则您可以删除您创建的所有其他非聚集索引,它们不会帮助此查询。


4
投票

您也应该尝试下面的代码

将记录插入到 临时表 不使用 Order by 子句

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

3
投票

对列建立索引无助于加快排序速度。

如果您想让查询快很多,请颠倒表的顺序。具体来说,首先在连接表中列出表

country

这有帮助的原因是 where 子句可以过滤第一个表中的行,而不必进行所有这些连接,然后过滤行。


2
投票

聚集索引中的字段按什么顺序包含?您需要将

startDateTime
字段放在第一位,以便
ORDER BY
与其匹配,或者在本例中,将
(countryId, startDateTime)
按该顺序放在前面,因为您想要选择单个
countryId
(间接地,通过
 countryName
)然后按
startDateTime
订购。

© www.soinside.com 2019 - 2024. All rights reserved.