不过,对这个缺陷,还是有个办法能够绕过去的,只要把上面给出的条件变变形,sql server还是能够变回到是用index seek, 而不是低性能的index scan. 具体请看我的英文原文吧(对不起了, 我一旦写了中文,就不想翻成英文,反过来也一样, 估计大家英文都还可以,实在不行的就看黑体部分吧, ): The seek predicate of the form "x > bookmark_of_x" is needed in paging related query. The compiler has no difficulty to parse it correctly if x is a single column index, or two columns index, however, if x is a three columns index or more, then the compiler will have a hard time to recognize it. This failure will result in that the seek predicate ended up in residue predicate, which results in a much worse execution plan. To illustrate the point, take a example, Create table A( a int, b int, c int, d float, primary key (a, b, c)) now check the plan for the query: select c, d from A where (a> 111 or a= 111 and (b > 222 or b = 222 and c > 333)) you can see a table scan op is used, and the Where clause ended up in residue predicate. However, if you rewrite the query in an equivalent form: select c, d from A where a> 111 or a= 111 and b > 222 or a= 111 and b= 222 and c >333 Then the compiler can choose an index seek op, which is desired. The problem is, the compiler should be able to recognize the first form of seek predicate on multiple columns index, it saves the user from having to pay extra time to figure out a get-around, not to mention the first form is a more efficient form of same expression. 上面的问题,可以说是部分的绕过去了,但是,也有绕不过的时候,接着看下面一段: It looks like that sql server lacks a consept of vector bookmark, or vector comparison or whatever you like to call it. The workaround is not a perfect workaround. If sql server were to understand the concept of vector bookmark, then the following two would be the same in execution plan and performance: 1. select top(n) * from A where vectorIndex >= @vectorIndex 2. select * from A where vectorIndex >= @vectorIndex and vectorIndex <=@vectorIndexEnd -- @vectorIndexEnd corresponds to the last row of 1. However, test has shown that, the second statement takes far more time than the first statement, and sql server actually only seek to the begining of the vector range and scan to the end of the whole Index, instead of stop at the end of the vector range. Not only sql server compile badly when the vector bookmark has 3 columns, test has shown that even with as few as 2 columns, sql serer still can not correctly recognize this is actually a vector range, example: 3. select top (100) a, b, c, d from A where a> 60 or a= 60 and b > 20 4. select a, b, c, d from A where (a> 60 or a= 60 and b > 20) and (a< 60 or a= 60 and b <= 21),
复制代码 代码如下: CREATE TABLE [dbo].[A]( [a] [int] NOT NULL, [b] [int] NOT NULL, [c] [int] NOT NULL, [d] [float] NULL, PRIMARY KEY CLUSTERED ([a] ASC, [b] ASC, [c] ASC) ) declare @a int, @b int, @c int set @a =1 while @a <= 100 begin set @b = 1 begin tran while @b <= 100 begin set @c = 1 while @c <= 100 begin INSERT INTO A (a, b, c, d) VALUES (@a,@b,@c,@a+@b+@c) set @c = @c + 1 end set @b = @b + 1 end commit set @a = @a + 1 end SET STATISTICS PROFILE ON SET STATISTICS time ON SET STATISTICS io ON
select top (10) a, b, c, d from A where (a> 60 or a= 60 and (b > 20 or b = 20 and c >= 31)) select a, b, c, d from A where (a> 60 or a= 60 and (b > 20 or b = 20 and c >= 31)) and (a< 60 or a= 60 and (b < 20 or b = 20 and c <= 40))
select top (10) a, b, c, d from A where a> 60 or a= 60 and b > 20 or a= 60 and b= 20 and c >= 31 select a, b, c, d from A where (a> 60 or a= 60 and b > 20 or a= 60 and b= 20 and c >= 31) and (a< 60 or a= 60 and b < 20 or a= 60 and b= 20 and c <= 40) select top (100) a, b, c, d from A where a> 60 or a= 60 and b > 20 select a, b, c, d from A where (a> 60 or a= 60 and b > 20) and (a< 60 or a= 60 and b <= 21) select top (100) a, b, c, d from A where a> 60 or a= 60 and b > 20 select a, b, c, d from A where (a> 60 or a= 60 and b > 20) and (a< 60 or a= 60 and b <= 21)