在两个列范围内选择数值

6

我有一个像这样的表格:

i1   i2
----------
1    a
1    b
1    c
1    d
2    x
3    y
4    a
4    b
4    c

我希望选择介于 1 c 和 4 a 之间的行。
结果应该如下所示:
1 c
1 d 
2 x 
3 y 
4 a

我该怎么做?


2
你使用哪个关系型数据库管理系统? - Giorgos Betsos
Sql Server 2014 - Ana
1
标准SQL:where (i1, i2) >= (1, 'c') and (i1, i2) <= (4, 'a'),但我不知道SQL Server是否支持。 - user330315
1
@a_horse_with_no_name:可惜,不行。 - onedaywhen
无论你在做什么,你都做错了。 - Salman A
4个回答

5
如果您使用支持行号功能的数据库,则一种选项是使用表和行号创建一个CTE,根据您指定的顺序(即首先按i1升序,然后按i2升序)排序。 然后,使用两个子查询来识别1c4a的行号。 这些行号构成您要选择的范围。
;WITH cte AS (
    SELECT ROW_NUMBER() OVER (ORDER BY i1, i2) AS RowNumber, i1, i2
    FROM yourTable
)

SELECT *
FROM cte t
WHERE t.RowNumber >= (SELECT RowNumber FROM cte WHERE i1=1 AND i2='c') AND
      t.RowNumber <= (SELECT RowNumber FROM cte WHERE i1=4 AND i2='a')

因为一个 where 子句就足够了,所以这个查询的做法过于冗长。 - Gordon Linoff
1
@GordonLinoff 在 Stack Overflow 这里,我们喜欢确保问题被完全解决。因此需要过度处理这个查询 :-) - Tim Biegeleisen
如果您已经在使用一个CTE,为什么不使用更多的CTE而不是子查询呢? - onedaywhen
@onedaywhen 如果我们只计划运行实际查询一次或几次,那么将子查询转换为CTE是否真的会带来很大的性能提升呢? - Tim Biegeleisen
我想我的意思是从风格/可读性的角度来看,即当您可以在子查询中使用CTE并且您已经在查询中使用CTE时,为什么不这样做呢? - onedaywhen

5
我会这样做:

我会这么做:

select t.*
from t
where (i1 > 1 or (i1 = 1 and i2 >= 'c')) and
      (i1 < 4 or (i1 = 4 and i2 <= 'a'));

我没想到这是可能的。 - Tim Biegeleisen

3
当然这并不是最优解决方案,但以下查询应该能够实现:
select i1, i2
from tbl
where (i1 > 1 and i1 < 4)
    or (i1 = 1 and i2 >='c')
    or (i1 = 4 and i2 <='a')

请注意,结果中包括(1, c)和(4, a)。如果您不需要包括边界,请更改比较运算符。

3

不是很美观的方法...但是

create procedure GetRangeBetween (@i11 int, @i12 char, @i21 int, @i22 char)
AS
BEGIN

if object_id ('tempdb..#Test') is not null drop table #Test

create table #Test (i1 int, i2 nvarchar(10), [Rank] int)

insert into #Test(i1,   i2)
values
(1,   'a'),  (1,    'b'), (1,    'c'),
(1,    'd'), (2,    'x'), (3,    'y'),
(4,    'a'), (4,    'b'), (4,    'c')

update #Test
    set Rank = src.[srcRank]
from #Test t
    join (select *, row_number() over (order by i1) [srcRank] from #Test) src 
        on t.i1 = src.i1 and t.i2 = src.i2

declare @Rank1 int = (select [Rank] from #Test where i1 = @i11 and i2 = @i12)
declare @Rank2 int = (select [Rank] from #Test where i1 = @i21 and i2 = @i22)

select i1, i2 from #Test
    where (i1 between @i11 and @i21) and ([Rank] between @Rank1 and @Rank2) 
END

接下来,您只需使用...来执行它。

execute GetRangeBetween 1, 'c', 4, 'a'

“[Rank] between 3 and 7” 这个语句是从哪里来的? - default locale
在嵌套的select语句中,使用row_number()函数可以为外部select语句提供[Rank]列。 - Veljko89
是的,但我们不能确定(1,'c')是否为第3名。我们需要在某个地方找到排名范围。 - default locale
1
已修复,不再需要担心等级问题,因为存储过程将始终正常工作 :) - Veljko89
但是,为每个调用重新计算所有值的额外列似乎有些过度。请查看Tim Biegeleisen上面的答案,以获取类似想法的更优雅的实现。 - default locale

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接