考虑一个包含复合主键的表:
create table T (
C1 int not null,
C2 int not null,
...
primary key (C1, C2)
)
以及检索大于某个主键元组的行的问题:
select * from T where (C1, C2) > (10, 100)
上面的伪语法在MSSQL和Oracle上无效。它可以实现为:
select * from T where (C1 = 10 and C2 > 100) or (C1 > 10)
PK拥有的列越多,这就越丑陋。有没有优雅的方法(MSSQL和Oracle)?
此外,利用(C1,C2)上的唯一索引非常重要。我不希望连接字符串中的列来模拟元组,然后最终进行表扫描。
答案 0 :(得分:0)
afair没有优雅而快速的方法,如果你真的只有2个int列 - 尝试将它们组合成一个bigint,但是对于更多 - 你将需要产生如此丑陋的条件,好消息 - 它将使用主键索引,并将很快工作
答案 1 :(得分:0)
如果您将问题表JOIN
转移到另一个表(例如临时表或变量表),您可以非常有效地获得您正在寻找的结果。我把这样一个简单的测试放在一起:
CREATE TABLE KeyTest
(
C1 INT NOT NULL
, C2 INT NOT NULL
CONSTRAINT PK_KeyTest PRIMARY KEY CLUSTERED (C1, C2)
);
INSERT INTO KeyTest (C1, C2) VALUES (
CAST((RAND() * 1000E0) AS INT),
CAST((RAND() * 10000E0) + 1000 AS INT));
GO 100
SELECT * FROM KeyTest; /* make note of a sample row for the next step
in my example, a randomly chosen row had
C1 = 275 and C2 = 6367
*/
DECLARE @TestFilter TABLE
(
C1 INT NOT NULL
, C2 INT NOT NULL
);
INSERT INTO @TestFilter (C1, C2) VALUES (275,6366);
SELECT KT.*
FROM KeyTest KT
INNER JOIN @TestFilter TF ON KT.C1 = TF.C1 AND KT.C2 >= TF.C2;
这将返回与C1匹配且大于或等于C2的所有行,其中包含@TestFilter
中包含的值,在我的示例275和6366中。结果:
您可以轻松更改此选项以返回复合键在C1上匹配大于10的值的所有行:
INSERT INTO @TestFilter (C1, C2) VALUES (10,0);
SELECT KT.*
FROM KeyTest KT
INNER JOIN @TestFilter TF ON KT.C1 > TF.C1;
答案 2 :(得分:0)
您正在寻找的是胶合键的比较:
'11000' > '10100' '10101' > '10100' '10099' < '10100' '09999' < '10100'
要实现这一目标,您需要一个虚拟列。在Oracle中,您只需构建一个功能索引,在SQL Server中,您将首先创建一个计算列并在其上构建索引。
您可以编写一个确定性(!)粘合功能来将您的密钥粘合在一起并使用它:
alter table T add column GluedKeys as GlueTKeys(c1,c2,c3,c4,c5);
create index IndexGluedTKeys on T(GluedKeys);
select * from T where GluedKeys > GlueTKeys(?,?,?,?,?);
请注意单个键的长度。不要粘合10 + 100到10100和20 + 10到2010,而是20 + 10到20010。
答案 3 :(得分:0)
在您的问题的评论中,您注意到在您的实际情况中,SQL是动态的。那为什么甚至打扰?您可以在循环中轻松创建n个键的语句,生成“...(c1> 10)或(c1 = 10和c2> 100)或(c1 = 10且c2 = 100且c3> ... “。
这可能会也可能不会使用您的pk索引。我敢说这么多,很可能不会被使用。你必须强迫它:
select *
from T with (index(pk_t))
where ...