尽管有序列被索引,为什么我的SQL Server ORDER BY会变慢?

时间:2011-06-03 05:04:46

标签: sql sql-server sql-server-2005 linq-to-entities

我有一个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秒。

我使用SQL Server Management Studio在startDatetime列上添加索引,并在“cityId,industryId,startDatetime,positionTypeId,payPerId,stateId”上使用聚簇索引(即“作业”中的所有列我们在JOINs和我们使用ORDER BY的列上使用)。我已经在JOIN中使用的每个列上都有单独的索引。不幸的是,这并没有使查询更快。

我跑了一个showplan并得到了:

   |--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)

重要的一行似乎是“| - 排序(ORDER BY :( [mydb]。[dbo]。[jobs]。[issueDatetime] ASC))” - 没有提到该列的索引。< / p>

为什么我的ORDER BY使我的查询变得如此慢,我怎样才能加快查询速度?

5 个答案:

答案 0 :(得分:12)

如果您的查询在此之前不包含订单,则会在找到的任何订单中返回数据。当您再次运行查询时,无法保证数据甚至会以相同的顺序返回。

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

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

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

答案 1 :(得分:7)

因为您的查询会投影所有列(*),所以它需要5列用于连接条件,并且对于可能是连接表列的内容具有非选择性WHERE子句,它会导致它被命中Index Tipping Point:优化器决定扫描整个表的成本较低,对其进行过滤并对其进行排序,以便对索引进行范围扫描,然后查找表中的每个键以检索所需的额外列(联接的5和*的其余部分。

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

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

Jeffrey建议使聚簇索引100%覆盖查询并肯定会提高性能,但更改聚簇索引会产生许多副作用。我将从上面的非聚集索引开始。除非其他查询需要它们,否则您可以删除您创建的所有其他非聚集索引,它们将无法帮助您进行此查询。

答案 2 :(得分:2)

聚集索引中的字段包含在哪个顺序?您需要先放置startDateTime字段,以使ORDER BY与其匹配,或者在此情况下(countryId, startDateTime)按此顺序排在前面,因为您要选择单个countryId 1}}(间接地,通过countryName)然后按startDateTime排序。

答案 3 :(得分:2)

你也应该尝试以下代码

将记录插入临时表而不使用订购条款

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

答案 4 :(得分:1)

新闻快讯:索引列无助于提高排序速度。

如果您想使查询A LOT更快地反转表格的顺序。具体来说,首先在联接表中列出表country。原因? where子句可以过滤第一个表中的行,而不必进行所有这些连接,然后过滤行。