在我们的数据库中,我们有200.000行
的表CREATE TABLE dbo.UserTask (
UserTask_ID int NOT NULL IDENTITY (1, 1),
UserTask_SequenceNumber int NOT NULL DEFAULT 0,
UserTask_IdEntitat uniqueidentifier NOT NULL,
UserTask_Subject varchar(100) NOT NULL,
UserTask_Description varchar(500) NOT NULL,
.....
.....
CONSTRAINT [PK_UserTask] PRIMARY KEY CLUSTERED
(
[UserTask_ID] ASC
) ON [PRIMARY]
) ON [PRIMARY]
我已使用
在UserTask_IdEntitat
列上创建了索引
CREATE NONCLUSTERED INDEX IX_UserTask_IDEntitat ON dbo.UserTask
(
UserTask_IDEntitat
)
执行以下查询,执行计划向我们显示UserTask_IDEntitat
上的索引用于执行查询:
SELECT UserTask_ID
FROM UserTask
WHERE UserTask_IdEntitat = @IdEntitat
ORDER BY UserTask_LastSendSystemDateTime desc
但是如果我们在Select
列表中添加另一列,则不使用索引
SELECT UserTask_ID, UserTask_SequenceNumber, UserTask_IDEntitat, ....., UserTask_Subject
FROM UserTask
WHERE UserTask_IdEntitat = @IdEntitat
ORDER BY UserTask_LastSendSystemDateTime desc
为什么添加与主键不同的列会导致SQL Server执行计划不使用UserTask_IDEntitat
列上的索引?
在此链接http://bytes.com/topic/sql-server/answers/144592-sqlsever-not-using-index之后,似乎是在列上重复过滤后的值的次数,它可以使索引不被使用,但我尝试使用@IdEntitat值进行查询重复60.000次,其他只重复175次,结果相同,忽略IDEntitat
列上的索引。
这让我发疯了!!!
感谢您的帮助。
答案 0 :(得分:45)
好的 - 只要您只选择索引中的列,或者选择群集键中的某些内容(通常是主键),就会使用索引,因为SQL Server可以找到所有信息需求(UserTask_IDEntitat
列和聚簇索引列)索引导航结构的叶级别。因此,它可以直接从索引的叶级页面返回该SELECT
查询所需的数据。
但是:如果您需要选择第二列,即索引定义中的 ,也不是群集密钥的一部分,那么SQL Server必须在实际的数据页面中进行所谓的书签查找。
因此 对于非聚集索引中找到的每一行 ,它必须获取聚簇索引值,搜索聚簇索引以查找实际数据页面该聚簇索引的叶级别,然后选择您想要的那一列。
书签查找非常适合少量点击 - 如果您选择了数千行,它们对性能来说是非常具有破坏性的。在这种情况下,SQL Server查询优化器正确地使用聚簇索引扫描 - 因为在聚集索引中,在叶级别上,它立即可用所有行
所以:如果你有一个UserTask_IDEntitat
的索引而你有时需要第二列UserTask_SequenceNumber
- 那么你可以在你的非聚集索引中包含该列:
CREATE NONCLUSTERED INDEX IX_UserTask_IDEntitat
ON dbo.UserTask(UserTask_IDEntitat)
INCLUDE(UserTask_SequenceNumber)
这样,该附加列仅存在于该非聚集索引的叶级中(不能在WHERE
子句中使用 - 它不是导航结构的一部分索引!) - 并且可以从非聚集索引的叶级节点再次满足您的第二个SELECT
- >不需要昂贵的书签查找 - >您的索引将再次使用。
长话短说,除非您的非聚集索引 高度选择性 (例如,返回1%或更少的行),除非您的非聚集索引是覆盖索引(包含满足特定查询所需的所有列的索引),然后SQL Server将 NOT 使用非聚簇索引的更改非常高
了解更多信息:
答案 1 :(得分:2)
您可以使用查询中的查询提示来使用Index。以下是更多详细信息的链接: http://msdn.microsoft.com/en-us/library/ms181714.aspx
答案 2 :(得分:0)
当相同的查询在不同的数据库上制定不同的计划时,我遇到了这种情况。在一个数据库上,它使用非聚集索引;在另一个数据库上,它使用表扫描。
此索引在INCLUDE中也不包含所有字段,最好的解决方案是将所有必需的选定字段添加到索引INCLUDE中。就我而言,空投缓存很有帮助。
有时,如果查询计划生成器的碎片超过50%,则它会忽略索引,因为查找索引中的行要比扫描整个表花费更多的时间。