我有一个奇怪的事情继续查询。我有一个包含两个bigint的表,StartNum和EndNum定义了数字范围。我有这两列的索引。查询采用给定的数字并返回它所属的范围。 WHERE子句是where @Num >= StartNum and @Num <= EndNum
。
在搜索列表开头附近的数字和接近结尾的数字之间,Profiler有很大的不同。列表中的数字越远,读取和持续时间就越多。查询计划显示它正在我的索引上使用搜索。
由于索引是一棵平衡的树,当然不应该有那么大的差异。有人可以向我解释一下吗?
小字: 这是在SQL 2005 Workgoup版本上。表中有大约200,000行。索引是非聚簇的(聚簇索引位于标识列上,但数据是以StartNum顺序插入的)。该索引有716页,深度为3,碎片为3%。
答案 0 :(得分:1)
这取决于您的查询。如果您只是查询这两个值,例如:
SELECT StartNum, EndNum
FROM Books
WHERE (@Num >= StartNum) AND (@Num <= EndNum)
在这种情况下,SQL Server只需要搜索两个索引来返回数字。你的两个索引包含你被索引的值,因为它是一个索引,它们被排序。
但我确定您确实在查询中包含了其他列:
SELECT BookID, Title, IDBN, Author, StartNum, EndNum
FROM Books
WHERE (@Num >= StartNum) AND (@Num <= EndNum)
在这种情况下,SQL Server一旦找到符合条件的行的
除了它已经从两个索引中获得的值之外:
注意: StartNum上的索引隐含地包含聚簇键的值,因为它知道哪个行对应于索引中的条目。
问题在于,如果有太多的书需要在表格中查找,那么从上到下阅读整个表格可能会更快。
类比:你可以在书的索引中查看所有对“设计模式”的引用。
设计模式:4,89,221,442
如果只有4个条目,那么您可以向后翻到索引中列出的页面。这称为书签查找。
但是,如果索引说有827个短语引用怎么办?
电脑:1,2,6,[snip 825 entries],1087,1128
然后阅读本书以便自己找到它们可能会更快。在这种情况下,我们放弃并扫描整本书。如果表具有聚簇索引,SQL Server将其称为聚簇索引扫描,如果没有聚簇索引(即它是“堆表”),则称为表扫描
如果您只是在查询中引用 StartNum 和 EndNum (假设您正在计算),那么我相信您会看到始终如一的低读数和执行时间。但是如果你包含其他列,并且SQL Server认为从该查询返回的行太多(例如,超过表的5%),那么它只会忘记索引并扫描整个表
有一个交叉点,SQL Server知道表中 StartNum 和 EndNum 值的分布,因为它会对值进行采样并对其分布进行统计。如果@Num的某些值碰巧返回几行,并且SQL Server知道这一点,它将执行相对较少的书签查找。但是,如果您的数据分发会导致更多行返回,那么您将获得聚簇索引扫描。
答案 1 :(得分:0)
您是否在StartNum ASC,EndNum DESC上编入索引?
因为如果你在StartNum ASC,EndNum ASC上编入索引,我预计它无法进行两次搜索。它必须进行搜索和扫描。
另一种可能性是在StartNum和EndNum上单独编制索引(顺序无关紧要),看看它是否会依次使用这两个索引然后进行某种连接。
在你看到不同的执行计划之前,你无法确定。
我无法在没有真正的列的情况下复制行为。我设置了一个200000 StartNum-EndNum范围100-199,200-299等的测试表。我已经能够让它进行聚簇索引扫描,表扫描和非聚集索引搜索,但没有接近你所说的。
答案 2 :(得分:0)
破解了!
EndNum总是&gt; = StartNum所以我只需找到最大的StartNum,即&lt; = @Num。我将索引更改为StartNum desc,将查询更改为top 1 ... where StartNum <= @Num order by StartNum desc
。现在对于@Num的任何值,它只是一个5次读取和0次持续时间的索引搜索,这正是我所追求的。
谢谢你的帮助,伙计们。