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)
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.
精彩评论