获取两个多列范围之间的行?

时间:2013-09-12 22:13:03

标签: sql-server sql-server-2008

我有下表。

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只有两行,它们给出了下限和上限。例如。

A B C D Upper
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 个答案:

答案 0 :(得分: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) ;

要在SQL-Server中添加此功能/语法(称为行值构造函数)Connect item,但由于它们仍在考虑将来的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 ;

答案 1 :(得分: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  

答案 2 :(得分:0)

@ypercube's suggestion更简洁但也更模糊的方法是:

对于每个T行:

  1. UNION所有包含Range表格的行。

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

  3. 明确地将2作为行号分配给T行,并将其与之前的结果进行INTERSECT。

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

  5. 或者在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(使查询更加冗长,当然)。

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

答案 3 :(得分:0)

根据RANGE表中的&#39; -value,它看起来像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的情况。