SQL Server非聚集索引永远不会出现在执行计划中
我有一个名为 tbl_event
的数据库表,其非聚集索引 IDX_Event_Folder
和 IDX_Event_Time
定义为:
CREATE NONCLUSTERED INDEX [IDX_Event_Folder]
ON [dbo].[tbl_event]([nobjectid] ASC)
CREATE NONCLUSTERED INDEX [IDX_Event_Time]
ON [dbo].[tbl_event]([tetime] ASC)
我运行了以下简单查询并得到了执行直接显示在下面的计划:
查询 1:
SELECT *
FROM tbl_event
WHERE tbl_event.nobjectid = 1410000
ORDER BY tetime
查询 2:
SELECT *
FROM tbl_event
WHERE tbl_event.nobjectid = 1410000
我的问题是,为什么索引在nobjectid上从未使用过?我希望当这些 select 语句的 where 子句中指定 nobjectid 时,会有索引查找或扫描。我对这个分析的理解是否错误?
I have a database table called tbl_event
with the non-clustered indexes IDX_Event_Folder
and IDX_Event_Time
defined as:
CREATE NONCLUSTERED INDEX [IDX_Event_Folder]
ON [dbo].[tbl_event]([nobjectid] ASC)
CREATE NONCLUSTERED INDEX [IDX_Event_Time]
ON [dbo].[tbl_event]([tetime] ASC)
I ran the following simple queries and got the execution plans displayed directly underneath:
Query 1:
SELECT *
FROM tbl_event
WHERE tbl_event.nobjectid = 1410000
ORDER BY tetime
Query 2:
SELECT *
FROM tbl_event
WHERE tbl_event.nobjectid = 1410000
My question is, why is the index on nobjectid never utilized? I would expect there to be an index seek or scan when nobjectid is specified in the where clause of these select statements. Is my understanding of this analysis incorrect?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一个常见的误解!
一点是:由于您使用的是
SELECT *
,因此您需要表中的所有数据。所以最后,SQL Server 必须返回到实际的数据页并获取所有值。当发生索引查找并找到命中时,在这种情况下,SQL Server 必须执行书签查找 - 这是一个相当昂贵的操作。
由于这些操作相当昂贵,SQL Server 将尽可能避免它们 - 因此在许多情况下,将使用表扫描,因为最终,这比查找 nc 索引然后进行书签查找要快。
检查要点:
nobjectid
列的选择性如何?这里的这个听起来或多或少是一个唯一的 ID——那就太好了。如果您碰巧在选择性不强的列上有一个索引,那么查询优化器通常会忽略它(因为它必须检查太多行,所以表扫描最终会更快)表中有多少行?对于小型表(少于几千行),从一开始就进行表扫描通常要快得多
此外,从您使用“RID堆查找”的第一个执行计划来看,我得出的结论是您没有集群表格上的索引 - 立即添加一个!没有聚集键(因此使用堆而不是聚集表)也会减慢大量操作并降低非聚集索引的有效性。
尝试在“NUSE”列上添加聚集索引:
INT IDENTITY
是完美的候选者 -UNIQUEIDENTIFIER
或非常宽的复合列集是最佳选择最糟糕的。阅读有关选择正确聚集索引的所有信息,请访问 Kimberly特里普的博客A common misconception!
One point is: since you're using
SELECT *
, you want all data from the table. So in the end, SQL Server must go back to the actual data pages and fetch all the values.When an index seek occurs and finds a hit, then in this case, SQL Server has to do a bookmark lookup - a rather expensive operation.
And since those operations are rather expensive, SQL Server will try to avoid them if it can - so in many cases, a table scan will be used instead, since in the end, that's faster than seeking the nc index and then doing a bookmark lookup.
Points to check:
how selective is the
nobjectid
column? This one here sounds like a more or less unique ID - that would be good. If you happen to have an index on a column that would be not very selective, then often, the query optimizer will ignore it (since it would have to check too many rows already, so a table scan is quicker in the end)how many rows are there in the table?? For small tables (less than a few thousand rows), it's often much faster to do a table scan from the get go
Also, from your first execution plan with the "RID heap lookup", I would conclude you don't have a clustered index on the table - add one right away!! Not having a clustered key (thus having a heap instead of a clustered table) also slows down lots of operations and reduces the effectiveness of a non-clustered index.
Try to add your clustered index on a "NUSE" column:
INT IDENTITY
is a perfect candidate -UNIQUEIDENTIFIER
or a very wide compound set of columns are the worst. Read all about choosing the right clustered index at Kimberly Tripp's blog您在评论中说表中当前有 18325170 行,其中只有大约 30 行的 nobjectid=1410000。
即使您的 IDX_Event_Folder 索引已禁用,我也无法相信 SQL Server 会为那么多行选择此计划,并且行粗细表明它认为它正在处理可能是 1 行而不是 18325170!
所以我很确定您必须禁用自动更新统计信息?如果是这样,您将需要手动更新统计信息(或者最好启用此选项)
You say in the comments that there are 18325170 rows currently in the table, only about 30 of them have nobjectid=1410000.
Even if your
IDX_Event_Folder
index was disabled I cannot believe that SQL Server would choose this plan for that amount of rows and the line thickness indicates it thinks it is dealing with maybe 1 row not 18325170!So I'm pretty sure that you must have auto update statistics disabled? If so you will need to update the statistics manually (or preferably enable this option)
有几个重叠的事情发生
SELECT *
意思是“给我所有列”。优化器认为扫描表更有用。SELECT *
结合使用时使用索引?如果你有这个,那么索引将被使用(作为扫描)
或者这个,新索引很可能会被使用
我建议你阅读这些文章
这些文章也应该涵盖聚集索引的缺乏
There are several overlapping things going on
SELECT *
means "give me all columns". The optimiser decides it's more useful to scan the table instead.SELECT *
?If you had this, then the index will be used (as a scan)
Or this, the new index would most likely be used
I suggest you read these articles
The lack of clustered index should be covered by these articles too