Geo*_*rge 16 sql sql-server linq-to-entities sql-server-2005
我有一个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
Run Code Online (Sandbox Code Playgroud)
查询返回大约1200行,我认为这不是一个巨大的数额.不幸的是,它还需要大约16秒.如果没有ORDER BY,查询将花费<1秒.
我已经使用SQL Server Management Studio在startDatetime列上添加索引,并在"cityId,industryId,startDatetime,positionTypeId,payPerId,stateId"上使用聚簇索引(即我们在"作业"中使用的所有列JOIN和我们使用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)
Run Code Online (Sandbox Code Playgroud)
重要的一行似乎是"| - 排序(ORDER BY :( [mydb].[dbo].[jobs].[issueDatetime] ASC))" - 没有提到该列的索引.
为什么我的ORDER BY使我的查询变得如此慢,我怎样才能加快查询速度?
Sco*_*uns 13
如果您的查询在那时不包含订单,那么它将返回找到的任何数据.再次运行查询时,无法保证数据甚至会以相同的顺序返回.
当您包含order by子句时,dabatase必须按正确的顺序构建行列表,然后按该顺序返回数据.这可能需要大量额外的处理,这转化为额外的时间.
对您的查询可能返回的大量列进行排序可能需要更长的时间.在某些时候,你将耗尽缓冲区空间,数据库必须开始交换,并且性能将下降.
尝试返回较少的列(指定所需的列而不是Select*)并查看查询是否运行得更快.
因为您的查询会投影所有列(*
),所以连接条件需要5列,并且对于WHERE
可能是连接表列的内容具有非选择性子句,它会使其达到索引引爆点:优化程序决定它的成本更低扫描整个表,对其进行过滤并对其进行排序,以便对索引进行范围扫描,然后查找表中的每个键以检索所需的额外列(连接为5,其余为列*
).
部分覆盖此查询的更好的索引可能是:
CREATE INDEX ... ON .. (countryId, startDatetime);
Run Code Online (Sandbox Code Playgroud)
Jeffrey提出的建立聚簇索引的建议将覆盖100%的查询并肯定会提高性能,但更改聚簇索引会产生许多副作用.我将从上面的非聚集索引开始.除非其他查询需要它们,否则您可以删除您创建的所有其他非聚集索引,它们将无法帮助此查询.