3

考虑一个具有复合主键的表:

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

4 回答 4

1

如果你真的只有 2 个 int 列,那么没有优雅和快速的方法可以做到这一点 - 尝试将它们组合成一个 bigint,但更多 - 你将需要产生如此丑陋的 where 条件,好消息 - 它将使用 primary关键索引,并且会很快工作

于 2013-10-15T00:33:29.667 回答
0

在对您的问题的评论中,您说在您的实际情况下,SQL 是动态的。那么为什么还要打扰呢?您可以轻松地为循环中的 n 个键创建语句,生成 "... (c1 > 10) or (c1 = 10 and c2 > 100) or (c1 = 10 and c2 = 100 and c3 > ..." 。

这可能会也可能不会使用您的 pk 指数。我敢说有这么多 ors 很可能不会被使用。你将不得不强迫它:

select * 
from T with (index(pk_t))
where ...
于 2013-10-15T15:02:05.763 回答
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;
于 2013-10-15T01:29:33.967 回答
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。

于 2013-10-15T14:39:31.740 回答