Sql 提取每2个样本之间时间差最小的数据

Sql 提取每2个样本之间时间差最小的数据,sql,sql-server,sql-server-2012,Sql,Sql Server,Sql Server 2012,我有一张带有样本时间戳的表格: create table #OREN_TEMP(TS datetime) insert into #OREN_TEMP(TS) values ('2019-10-25 06:20:07.000'), ('2019-10-25 06:20:15.000'), ('2019-10-25 06:20:19.000'), ('2019-10-25 06:20:26.000'), ('2019-10-25 06:20:26

我有一张带有样本时间戳的表格:

create table #OREN_TEMP(TS datetime)

insert into #OREN_TEMP(TS)
    values 
    ('2019-10-25 06:20:07.000'),
    ('2019-10-25 06:20:15.000'),
    ('2019-10-25 06:20:19.000'),
    ('2019-10-25 06:20:26.000'),
    ('2019-10-25 06:20:26.000'),
    ('2019-10-25 06:20:34.000'),
    ('2019-10-25 06:20:42.000'),
    ('2019-10-25 06:20:51.000'),
    ('2019-10-25 06:20:59.000'),
    ('2019-10-25 06:21:07.000'),
    ('2019-10-25 06:21:15.000'),
    ('2019-10-25 06:21:19.000'),
    ('2019-10-25 06:21:26.000')

    select * from #OREN_TEMP
当一个样本到另一个样本之间的最短时间为20秒时,我被要求从表中提取数据。 这意味着我需要提取以下内容:

('2019-10-25 06:20:07.000')
('2019-10-25 06:20:34.000')
('2019-10-25 06:20:59.000')
('2019-10-25 06:21:19.000')
declare @MIN_DATE datetime

select top 1 @MIN_DATE =TS from #OREN_TEMP
order by TS
select TS
from (select TS, row_number() over (partition by datediff(second,@MIN_DATE,TS) / 20 order by TS) as RowNum
      from #OREN_TEMP
    ) TMP
where TMP.ROWNUM=1
我试着做到以下几点:

('2019-10-25 06:20:07.000')
('2019-10-25 06:20:34.000')
('2019-10-25 06:20:59.000')
('2019-10-25 06:21:19.000')
declare @MIN_DATE datetime

select top 1 @MIN_DATE =TS from #OREN_TEMP
order by TS
select TS
from (select TS, row_number() over (partition by datediff(second,@MIN_DATE,TS) / 20 order by TS) as RowNum
      from #OREN_TEMP
    ) TMP
where TMP.ROWNUM=1
我得到:

TS
2019-10-25 06:20:07.000
2019-10-25 06:20:34.000
2019-10-25 06:20:51.000 -- Not good - only 17 seconds from previous
2019-10-25 06:21:07.000 -- Not good - only 16 seconds from previous
但这并没有起到任何作用,因为我根据第一个TS划分时间框架,而不是根据最后一个TS划分时间框架

我想避免循环或光标。与表本身的内部联接是正常的

我该怎么做?
我使用的是SQL server 2012标准版。

您可以为此使用递归CTE。但是它在SQLServer中效率不高。SQL Server对递归CTE有许多限制,包括子查询中没有聚合和递归引用

因此,这里有一个版本:

with first_row as (
      select top (1) ot.*
      from oren_temp ot
      order by ts asc
     ),
     cte as (
      select ts, 1 as lev
      from first_row
      union all
      select ot.ts, lev + 1
      from oren_temp ot join
           cte
           on ot.ts >= dateadd(second, 20, cte.ts)
     )
select lev, min(ts)
from cte
group by lev;

不过,我要提醒您不要在行太多的数据集上运行此操作

编辑:

实际上,有一种更有效的方法。在每个递归步骤中,我们可以使用
row\u number()
保留前一个最先找到的
ts
(这是最小值):


对于较大的数据,这应该更有效。而且,它也很酷。

我的答案像@Gordon,但是独立制作的

我又用了cte

;with cte as ( 
-- got first row , mark it RowNumber = 1
select top 1 ts, RN = 1 
from #OREN_TEMP

union all
-- in recursive part set number for all rows
select  T.ts, convert(int,row_number() over ( order by T.TS)) 
from #OREN_TEMP T 
-- join only to rows which are after 20+ secs
join cte T1 on datediff(SECOND,T1.TS, T.TS) >= 20
-- and we have to take RowNumber = 1 from cte
and T1.RN = 1
)
-- and result goes.......
select TS from cte
where RN = 1
结果

ts
-----------------------
2019-10-25 06:20:07.000
2019-10-25 06:20:34.000
2019-10-25 06:20:59.000
2019-10-25 06:21:19.000
编辑

对于像我(俄罗斯)这样的国家,要运行插入,我们必须使用命令预先编写代码

SET DATEFORMAT mdy

你看过
领先
滞后
吗?谢谢戈登。它正在工作!!!你能解释一下为什么在递归内部和外部需要“where seqnum=1”吗?非常欢迎对代码进行简要解释。@oren。事实上,我想是的。在递归的每一步中,都有许多行出现或超过20秒的限制。
seqnum=1
在下一轮递归中进行过滤,只考虑第一行。