我有一个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使我的查询变得如此慢,我怎样才能加快查询速度?
答案 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子句可以过滤第一个表中的行,而不必进行所有这些连接,然后过滤行。