我有下表。
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
之间的所有行的最简洁方法是什么?
答案 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
行:
UNION所有包含Range
表格的行。
按A, B, C, D
的顺序将行号分配给结果集。
明确地将2
作为行号分配给T
行,并将其与之前的结果进行INTERSECT。
如果交叉点不为空,则返回该行。
或者在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的情况。