我有一张桌子
T (variable_name, start_no, end_no)
包含如下值:
(x, 10, 20)
(x, 30, 50)
(x, 60, 70)
(y, 1, 3)
(y, 7, 8)
所有间隔都保证不相交。
我想在T-SQL中编写一个查询,用于计算未搜索变量的时间间隔:
(x, 21, 29)
(x, 51, 59)
(y, 4, 6)
我可以不用光标吗?
我正在考虑通过variable_name进行分区,然后按start_no进行排序。但接下来该怎么办呢?给定行集中的当前行,如何访问“下一行”?
答案 0 :(得分:8)
由于您没有指定哪个版本的SQL Server,我有多个解决方案。如果你还在摇摆SQL Server 2005,那么Giorgi就可以很好地使用CROSS APPLY了。
注意:对于这两种解决方案,我使用where子句来过滤掉不正确的值,因此即使数据不正确且行重叠,也会忽略这些值。
IndexError: Cursor instances do not support negative indices
DECLARE @T TABLE (variable_name CHAR, start_no INT, end_no INT)
INSERT INTO @T
VALUES ('x', 10, 20),
('x', 30, 50),
('x', 60, 70),
('y', 1, 3),
('y', 7, 8);
SELECT *
FROM
(
SELECT variable_name,
LAG(end_no,1) OVER (PARTITION BY variable_name ORDER BY start_no) + 1 AS start_range,
start_no - 1 AS end_range
FROM @T
) A
WHERE end_range > start_range
答案 1 :(得分:3)
以下是cross apply
的另一个版本:
DECLARE @t TABLE ( v CHAR(1), sn INT, en INT )
INSERT INTO @t
VALUES ( 'x', 10, 20 ),
( 'x', 30, 50 ),
( 'x', 60, 70 ),
( 'y', 1, 3 ),
( 'y', 7, 8 );
SELECT t.v, t.en + 1, c.sn - 1 FROM @t t
CROSS APPLY(SELECT TOP 1 * FROM @t WHERE v = t.v AND sn > t.sn ORDER BY sn)c
WHERE t.en + 1 < c.sn
答案 2 :(得分:0)
对于每个end_no
,您应找到最近的start_no
&gt; end_no
然后排除没有最近start_no
的行(variable_name
的最后一行)
WITH A AS
(
SELECT variable_name, end_no+1 as x1,
(SELECT MIN(start_no)-1 FROM t
WHERE t.variable_name = t1.variable_name
AND t.start_no>t1.end_no) as x2
FROM t as t1 )
SELECT * FROM A WHERE x2 IS NOT NULL
ORDER BY variable_name,x1
这也是我对类似问题的旧回答:
答案 3 :(得分:0)
这是非常便携的,因为它不需要CTE或分析功能。如果有必要,我也可以在没有派生表的情况下轻松地重写。
select * from (
select
variable_name,
end_no + 1 as start_no,
(
select min(start_no) - 1
from T as t2
where t2.variable_name = t1.variable_name and t2.start_no > t1.end_no
) as end_no
from T as t1
) as intervals
where start_no <= end_no
补充间隔的数量最多比您开始时的数量少一个。 (如果两个范围实际上是连续的,那么有些将被消除。)因此,很容易取出每个单独的间隔并计算右边的一个(或者如果你想要反转某些逻辑则左边。)
答案 4 :(得分:0)
这是一个似乎有用的非CTE版本:http://sqlfiddle.com/#!9/4fdb4/1
鉴于保证不相交的范围,我只是将T加到自身,计算下一个范围作为相邻范围的增量/减量,然后确保新范围不与任何现有范围重叠。
select t1.variable_name, t1.end_no+1, t2.start_no-1
from t t1
join t t2
on t1.variable_name=t2.variable_name
where t1.start_no < t2.start_no
and t1.end_no < t2.end_no
and not exists (select *
from t
where ((t2.start_no-1< t.end_no
and t1.end_no+1 > t.start_no) or
(t1.end_no + 1 < t.end_no and
t2.start_no-1 > t.end_no))
and t.variable_name=t1.variable_name)