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

Sql server 获取两个多列之间的行范围?,sql-server,sql-server-2008,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)) 表格范围只有两行,

我有下表

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))
表格范围只有两行,这两行给出了下限和上限。比如说

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的自然顺序)的最简洁方法是什么?

在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中添加了一种称为行值构造函数的功能/语法,但由于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 ;

要做到这一点,一种更简洁但也更模糊的方法是:

对于每个T行:

将所有行与范围表合并

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

将2显式指定为T行的行号,并将其与上一个结果相交

如果交叉点不是空的,则返回该行

或在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行确实排序在指定的下限之后,而在上限之前,则T行将被分配数字2,因此与自身相交的数字2显式分配应返回非空集。如果行超出边界,它将是1或3,因此与自身相交为2将导致一个空集。值将匹配,但行号不匹配

这里还有一点需要注意的是,确定哪一个边界的上列根本没有使用,而边界是由它们的排序方式自动确定的。我意识到这可能不适合你。例如,您可能希望识别边界分配不正确的情况,即下界实际排序在上界之后,因此将任何行与此类设置进行比较在逻辑上应导致错误。为了解决这个问题,您可能需要使ROW_NUMBER表达式的ORDER BY子句比A、B、C、D更复杂,或者,可能还需要使用一个额外的枚举来解释上限,当然,这会使查询更加详细


除了不是很清楚之外,这种方法在性能方面也可能不是很有效,但是您可能需要自己验证。尽管如此,问题还是在于找到一种简洁的方法,现在就在这里。

根据表范围中的“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时才起作用。

你能举一个例子说明中间值的含义吗?您是在询问表中出现在这两行之间的所有行吗?如果B=2表示下限值,B=1表示上限值没有意义,那么您是否在逐列询问值?或者别的什么?对于a来说,3小于9。
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)