SQL-如果差异低于阈值,则合并两行

SQL-如果差异低于阈值,则合并两行,sql,sql-server,tsql,Sql,Sql Server,Tsql,我在SQL Server中有这样一个表: id start_time end_time 1 10:00:00 10:34:00 2 10:38:00 10:52:00 3 10:53:00 11:23:00 4 11:24:00 11:56:00 5 14:20:00 14:40:00 6 14:41:00 14:59:00 7 15:30:00 15:40:00 我想要的是一个查询,它根据第n行的两个连续记录end_t

我在SQL Server中有这样一个表:

id  start_time  end_time
1   10:00:00    10:34:00
2   10:38:00    10:52:00
3   10:53:00    11:23:00
4   11:24:00    11:56:00
5   14:20:00    14:40:00
6   14:41:00    14:59:00
7   15:30:00    15:40:00
我想要的是一个查询,它根据第n行的两个连续记录end_time和第n+1行的start_time之间的时间差输出合并记录。时间差小于2分钟的所有记录应合并为一个时间条目,并保留第一条记录的ID。如果多个连续记录的时差小于2分钟,则还应合并两个以上的记录

这将是预期的产出:

id  start_time  end_time
1   10:00:00    10:34:00
2   10:38:00    11:56:00
5   14:20:00    14:59:00
7   15:30:00    15:40:00
提前感谢您提供有关如何构建查询的任何提示

编辑: 我从下面的代码开始计算提前期和时差,但不知道如何分组和合并

WITH rows AS
        (
        SELECT  *, ROW_NUMBER() OVER (ORDER BY Id) AS rn
        FROM #temp
        )
SELECT  mc.id, mc.start_time, mc.end_time, mp.start_time lead_time, DATEDIFF(MINUTE, mc.[end_time], mp.[start_time]) as DiffToNewSession
FROM    rows mc
LEFT JOIN    rows mp
ON      mc.rn = mp.rn - 1
您可以使用递归cte来获得所需的结果。这种方法只是简单地比较当前结束时间和下一个开始时间。如果小于2分钟阈值,则使用与grp\U start相同的启动时间。最后,简单地在grp_开始时进行分组


我想这应该在没有递归的情况下完成。为了使解决方案更易于阅读,我再次使用了几个CTE。我想可以减少一点

INSERT INTO T1 VALUES
(1,'10:00:00','10:34:00')
,(2,'10:38:00','10:52:00')
,(3,'10:53:00','11:23:00')
,(4,'11:24:00','11:56:00')
,(5,'14:20:00','14:40:00')
,(6,'14:41:00','14:59:00')
,(7,'15:30:00','15:40:00')
GO

WITH cte AS(
SELECT *
      ,ROW_NUMBER() OVER (ORDER BY id) AS rn
      ,DATEDIFF(MINUTE, ISNULL(LAG(endtime) OVER (ORDER BY id), starttime), starttime) AS diffMin
      ,COUNT(*) OVER (PARTITION BY (SELECT 1)) as maxRn
  FROM T1
),
cteFirst AS(
SELECT *
  FROM cte
  WHERE rn = 1 OR diffMin > 2
),
cteGrp AS(
SELECT *
      ,ISNULL(LEAD(rn) OVER (ORDER BY id), maxRn+1) AS nextRn
  FROM cteFirst
)
SELECT f.id, f.starttime, MAX(ISNULL(n.endtime, f.endtime)) AS endtime
  FROM cteGrp f
  LEFT JOIN cte n ON n.rn >= f.rn AND n.rn < f.nextRn
  GROUP BY f.id, f.starttime

t-sql中的窗口函数可以实现大量的数据统计,如

create table #temp(id int identity(1,1), start_time time, end_time time)
insert into #temp(start_time, end_time)
values  ('10:00:00', '10:34:00')
      , ('10:38:00', '10:52:00')
      , ('10:53:00', '11:23:00')
      , ('11:24:00', '11:56:00')
      , ('14:20:00', '14:40:00')
      , ('14:41:00', '14:59:00')
      , ('15:30:00', '15:40:00')

;with c0 as(
select *, LAG(end_time,1,'00:00:00') over (order by id) as lag_time
from #temp
), c1 as(
select *, case when DATEDIFF(MI, lag_time, start_time) <= 2 then 1 else -0 end as gflag
from c0
), c2 as(
select *, SUM(case when gflag=0 then 1 else 0 end) over(order by id) as gid
from c1
)
select MIN(id) as id, MIN(start_time) as start_time, MAX(end_time) as end_time
from c2
group by gid
为了更好地描述数据构造的过程,我简单地使用了c0,c1,c2。。。要表示级别,可以合并一些级别并进行优化。
如果不能使用id作为排序条件,则需要更改上述语句中的排序部分。

您可以查看。它使用Group by,并且可以工作。根据问题指南,请说明您尝试了什么,并告诉我们您在本网站或其他网站上找到了什么,以及为什么它不能满足您的需要。根据两个连续记录之间的时差-您错过了时差的定义,因为有两个时间列。@astentx,OP是指一行的结束时间与下一行的开始时间之间的差值。。。。这就是他如何得到ID=2,开始时间=10:30,结束时间=11:56的一行。。。。ID为3的行在ID为2的行结束后1分钟开始,第4行在第3行结束后1分钟开始,但第5行在第4行结束后24分钟开始,因此,在预期输出中,第5行获得其自己的行的原因是希望ID值严格连续…如果不是,则使用row_number生成新的sequenceworks,就像一个符咒。谢谢大家!@拉斐尔。注意:如果行未按时间排序,则此操作无效。你的问题并没有说明情况总是这样。函数接受3个参数:值、偏移量和默认值,当函数超出窗口边界时使用。因此,如果nullLeadExpr1…,则expr2可以仅用LEAD重写,这更具可读性:LEADexpr1,1,expr2…`这仅在不应合并最后两行时有效。如果最后一行的时差低于treshhold,则不会将其视为最后第二行的结束时间,也不会单独包含在结果中。工作正常。不过,对我来说,更容易理解递归解决方案。但是谢谢!欢迎您亲自使用集合思想来解决SQL问题,并强烈建议您在程序中更好地使用递归和循环游标。@Raffael。作为OP,你当然可以选择任何你想要的答案。为了清晰和性能,我选择了这个版本。在做了一些额外的工作并添加了排序标准之后,我有了一些其他的列,而不仅仅是我从递归解决方案更改为这个解决方案的ID,因为您可以很容易地一步一步地遵循数据构造。
create table #temp(id int identity(1,1), start_time time, end_time time)
insert into #temp(start_time, end_time)
values  ('10:00:00', '10:34:00')
      , ('10:38:00', '10:52:00')
      , ('10:53:00', '11:23:00')
      , ('11:24:00', '11:56:00')
      , ('14:20:00', '14:40:00')
      , ('14:41:00', '14:59:00')
      , ('15:30:00', '15:40:00')

;with c0 as(
select *, LAG(end_time,1,'00:00:00') over (order by id) as lag_time
from #temp
), c1 as(
select *, case when DATEDIFF(MI, lag_time, start_time) <= 2 then 1 else -0 end as gflag
from c0
), c2 as(
select *, SUM(case when gflag=0 then 1 else 0 end) over(order by id) as gid
from c1
)
select MIN(id) as id, MIN(start_time) as start_time, MAX(end_time) as end_time
from c2
group by gid