是否可以在where条件下使用列元组?

时间:2013-10-15 00:29:22

标签: sql sql-server oracle

考虑一个包含复合主键的表:

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)上的唯一索引非常重要。我不希望连接字符串中的列来模拟元组,然后最终进行表扫描。

4 个答案:

答案 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中。结果:

enter image description here

您可以轻松更改此选项以返回复合键在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 ...