0

我有下表。

create table T(
    A int, B int, C int, D int, X.... 
    primary key (A, B, C, D))

该表是按顺序排序的,A, B, C, D因为它们是聚集键列。我还有另一张桌子

create table Range(
    A int, B int, C int, D int, Upper bit not --  0: lower, 1: upper
    primary key (A, B, C, D))

该表Range只有两行,给出了下限和上限。例如。

ABCD 上
3 2 9 5 0
9 1 4 1 1

编写查询以获取 之间的所有行A:3 B:2 C:9 D:5 (3295)以及A:9 B:1 C:4 D:1 (9141)按 的自然顺序获取所有行的最简洁方法是A, B, C, D什么?

4

4 回答 4

2

在 Postgres 和 Oracle 中,你可以这样做:

SELECT *
FROM t
WHERE (a,b,c,d) BETWEEN (SELECT a,b,c,d FROM Range WHERE upper = 0)
                    AND (SELECT a,b,c,d FROM Range WHERE upper = 1) ;

此功能/语法的Connect 项(称为行值构造函数)要添加到 SQL-Server 中,但由于他们仍在考虑将其用于 SQL Server 的未来版本(现在已经 6 年),因此您必须使用一些不太优雅的条件,例如:

SELECT t.*
FROM t
  JOIN 
    (SELECT a,b,c,d FROM Range WHERE upper = 0) AS rlow
      ON rlow.a = t.a AND rlow.b = t.b AND rlow.c = t.c AND rlow.d <= t.d
      OR rlow.a = t.a AND rlow.b = t.b AND rlow.c < t.c
      OR rlow.a = t.a AND rlow.b < t.b
      OR rlow.a < t.a
  JOIN 
    (SELECT a,b,c,d FROM Range WHERE upper = 1) AS rhigh
      ON rhigh.a = t.a AND rhigh.b = t.b AND rhigh.c = t.c AND rhigh.d >= t.d
      OR rhigh.a = t.a AND rhigh.b = t.b AND rhigh.c > t.c
      OR rhigh.a = t.a AND rhigh.b > t.b
      OR rhigh.a > t.a ;
于 2013-09-13T08:57:41.133 回答
0
SELECT *  FROM T  
LEFT JOIN `RANGE` U  
ON ( U.Upper=0)  
LEFT JOIN `RANGE` L  
ON ( L.Upper=1)   
Where T.A between U.A and L.A  
        And T.B between U.B and L.B  
        And T.C between U.C and L.C  
        And T.D between U.D and L.D  
于 2013-09-13T06:47:47.390 回答
0

@ypercube 的建议更简洁但也更晦涩的方法如下:

对于每一T行:

  1. UNION ALL 与Range表的行。

  2. 按 的顺序将行号分配给结果集A, B, C, D

  3. 显式2地将行号分配给该T行并将其与先前的结果相交。

  4. 如果交集不为空,则返回该行。

或者在 SQL 中:

SELECT T.*
FROM T
WHERE EXISTS (
  SELECT T.A, T.B, T.C, T.D, 2
  INTERSECT
  SELECT *, r = ROW_NUMBER() OVER (ORDER BY A, B, C, D)
  FROM (
    SELECT T.A, T.B, T.C, T.D
    UNION ALL
    SELECT A, B, C, D
    FROM Range
  ) AS u
);

该方法背后的想法是,在枚举时,T如果该行确实在指定的下限之后和上限之前排序,则该行将被分配数字 2,因此与显式分配数字 2 的自身相交应该返回非空放。如果行超出范围,它将是#1 或#3,因此与#2 自身相交将导致一个空集(值将匹配,但行号不匹配)。

这里还有一个小问题,即Upper确定哪个边界根本不使用的列,并且边界由它们的排序方式自动确定。我意识到这可能不适合你。例如,您可能想要识别边界分配不正确的情况,即下限实际上排在上限之后,因此将任何行与这样的设置进行比较在逻辑上应该导致false. 为了解决这个问题,您可能需要使 ROW_NUMBER 表达式的 ORDER BY 子句更复杂,A, B, C, D或者,也许,使用额外的枚举来以Upper某种方式解释(当然,使查询更冗长)。

除了不太清楚之外,这种方法在性能方面也可能效率不高,但是您可能需要自己验证。不过,问题是要找到一种简洁的方法,所以你来了。

于 2013-12-05T21:30:11.807 回答
0

根据表 RANGE 中的“B”值,在我看来 OP 想要将行作为数字进行比较,所以最简单的方法是将列值乘以 1000(对于 A 列)、100 (用于 B 列)、10(用于 C 列)和 1(用于 D 列):

SELECT *  FROM T  
LEFT JOIN `RANGE` U  
ON ( U.Upper=0)  
LEFT JOIN `RANGE` L  
ON ( L.Upper=1)   
Where (T.A*1000 + T.B*100 + T.C * 10 + T.D) between (U.A*1000 + U.B*100 + U.C*10 + U.D) and (L.A*1000 + L.B*100 + L.C*10 + L.D)

当然,这只适用于 A、B、C 和 D 的值小于 10 的情况。

于 2016-07-21T12:23:40.717 回答