开发者

How do I get a simple range scan on a multikey index?

Using Microsoft SQL Server here. Given this set up (a clustered index on two columns):

use tempdb;
CREATE TABLE mytemp
(
    N1 INT NOT NULL,
    N2 INT NOT NULL,
    PRIMARY KEY (N1, N2)
);

INSERT mytemp (N1, N2)
SELECT N1.number, N2.number
FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS N1(number)
CROSS JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS N2(number)

How can I get the values after n1=5, n2=6 (when ordered by n1, n2)? I have the following try.

declare @n1boundary int = 5;
declare @n2boundary int = 6;

SELECT N1, N2
FROM mytemp
WHERE N1 = 开发者_高级运维@n1boundary AND N2 > @n2boundary
OR N1 > @n1boundary
ORDER BY N1, N2

It gives me the results I want, but the syntax seems clumsy and it performs two scans. It seems to me that in theory, it should be possible to do this with a single scan.

[Edit] The scans I'm looking at are the ones reported with SET STATISTICS IO set to ON:

Table 'mytemp'. Scan count 2, logical reads 4


I got a single seek operation when I tried it on my system.

Microsoft SQL Server 2008 (SP1) - 10.0.2531.0 (X64) Mar 29 2009 10:11:52 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition (64-bit) on Windows NT 5.2 (Build 3790: Service Pack 2) (VM)

How do I get a simple range scan on a multikey index?


When I run this I get a fairly good execution plan doing just a clustered index seek.

Another option is as below, using a composite key, but this does a clustered index SCAN so performance is probably not as good on a large table

    declare @n1boundary int = 5;
    declare @n2boundary int = 6;

    select N1, N2
    from mytemp 
    where cast(N1 as varchar(2))+cast((N2-1) as varchar(2)) >
 cast(cast(@n1boundary as varchar(2))+cast((@n2boundary-1) as varchar(2)) as int)


Based on PCurd's answer, here's another quirky work-around. It persists a concatenation of the clustered index, indexes it, and then does a simple range scan on it:

ALTER TABLE mytemp
ADD stringify as 
    right('00' + cast(n1 as varchar), 2) +
    right('00' + cast(n2 as varchar), 2) PERSISTED;

CREATE NONCLUSTERED INDEX ix_mytemp 
ON mytemp(stringify)
INCLUDE (n1, n2)

declare @n1boundary int = 5;
declare @n2boundary int = 6;

SELECT n1, n2
FROM mytemp
WHERE stringify > 
    right('00' + cast(@n1boundary as varchar), 2) +
    right('00' + cast(@n2boundary as varchar), 2) 
ORDER BY stringify;

It's sloppy, and not really what I was looking for either.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜