MSSQL获得时间差异小于X的行

时间:2018-03-07 13:41:59

标签: sql sql-server datediff self-join

我的表格中包含IDCHARDATETIME字段。现在我想得到所有行,DATEDIFF为5分钟或更短。

参考样本数据:

  ID2    CHA       Timer
   1       B      2018-03-06 11:31:39
   2       S      2018-03-06 11:33:39
   3       B      2018-03-06 11:39:39
   4       S      2018-03-06 11:45:39
   5       B      2018-03-06 11:46:39
   6       S      2018-03-06 11:47:39
   7       B      2018-03-06 11:48:39
   8       S      2018-03-06 11:50:39
   9       B      2018-03-06 11:51:39
   10      S      2018-03-06 11:59:39

期望的输出:

  ID2    CHA       Timer
   1       B      2018-03-06 11:31:39
   2       S      2018-03-06 11:33:39
   4       S      2018-03-06 11:45:39
   5       B      2018-03-06 11:46:39
   6       S      2018-03-06 11:47:39
   7       B      2018-03-06 11:48:39
   8       S      2018-03-06 11:50:39
   9       B      2018-03-06 11:51:39

我目前的疑问是:

select *
from t t1
inner join t t2
on t1.ID = t2.ID
where datediff(minute, t1.timer, t2.timer)<=5

可悲的是,这个多次返回相同的条目。我认为这是因为INNER JOIN而发生的,但我无法确定。

如何获得所需的结果?

Sqlfiddle自己测试一下。

4 个答案:

答案 0 :(得分:2)

<强>更新

以下是使用更新数据的新解决方案。

SELECT  *
  FROM  t AS t1
  WHERE EXISTS
      ( SELECT  1
          FROM  t AS t2
          WHERE ( t1.timer <= DATEADD( MINUTE, 5, t2.timer )
                  OR t1.timer >= DATEADD( MINUTE, -5, t2.timer ))
                AND t1.id <> t2.id)
;

这将返回在其之前或之后的5分钟内发生的另一行的任何行。如果您使用大量数据运行此查询,则应该能够在timer列上使用索引。

陈旧的答案

你非常接近。您需要加入字符字段,除非ID匹配。

select t2.*
from t t1
inner join t t2
on t1.cha = t2.cha
and t1.id <> t2.id
where datediff(minute, t1.timer, t2.timer) <=5
order by t2.id;

答案 1 :(得分:2)

您可以使用LEADLAG窗口函数:

select id, cha, timer
from (
  select id, cha, timer,   
         COALESCE(datediff(minute,                   
                           lag(timer) over (order by id),
                           timer) 
                  , 10) prev_diff,
         COALESCE(datediff(minute, 
                           timer, 
                           lead(timer) over (order by id))
                  , 10) next_diff
   from t) as x
where prev_diff <= 5 or next_diff <= 5 

LEAD用于获取 next 记录的timer值,而LAG用于获取 previous <的值/ em>记录。如果当前值与这两个值中的任何一个之间的差异等于或小于5,那么您就匹配了。

Demo here

<强>更新

如果id字段无法用于确定行顺序,则您可以使用ROW_NUMBER生成的数字代替:

;with t_rn AS (
   select id, cha, timer,
          row_number() over (order by timer) as rn
   from t
)
select id, cha, timer
from (
   select id, cha, timer,   
          coalesce(datediff(minute,                   
                            lag(timer) over (order by rn),
                            timer) 
                   , 10) prev_diff,
          coalesce(datediff(minute, 
                            timer, 
                            lead(timer) over (order by rn))
                   , 10) next_diff
   from t_rn) as x
where  prev_diff <= 5 or next_diff <= 5 

Demo here

感谢@Vladimir,他可以看到显而易见的地方,上面的查询可以简化为:

select id, cha, timer
from (
   select id, cha, timer,   
          coalesce(datediff(minute,                   
                            lag(timer) over (order by timer),
                            timer) 
                   , 10) prev_diff,
          coalesce(datediff(minute, 
                            timer, 
                            lead(timer) over (order by timer))
                   , 10) next_diff
   from t_rn) as x
where  prev_diff <= 5 or next_diff <= 5 

答案 2 :(得分:0)

好吧,您可以CROSS JOIN代替INNER JOIN

select *
from t t1
cross join t t2
where 
    ABS(datediff(minute, t1.timer, t2.timer))<=5
    AND t1.id < t2.id

这将给出所有可能的行对,其时间差小于5分钟。

t1.id < t2.id只需要返回每对中的一个实例。

如果您对这样的配对不感兴趣,那么,您只需要将配对的每一面都放在一个列表中。 UNION将删除重复项。

WITH
CTE_Pairs
AS
(
  select
    T1.id AS id1
    ,T1.cha AS cha1
    ,T1.timer AS timer1
    ,T2.id AS id2
    ,T2.cha AS cha2
    ,T2.timer AS timer2
  from t t1
  cross join t t2
  where 
      ABS(datediff(second, t1.timer, t2.timer)) <= 5*60
      AND t1.id < t2.id
)
SELECT 
  id1 AS id
  ,cha1 AS cha
  ,timer1 AS timer
FROM CTE_Pairs

UNION

SELECT 
  id2 AS id
  ,cha2 AS cha
  ,timer2 AS timer
FROM CTE_Pairs

ORDER BY id
;

答案 3 :(得分:0)

这应该可以解决问题

select distinct t1.*
from t t1
inner join t t2
on t1.ID <> t2.ID
where datediff(minute, t1.timer, t2.timer) between -5 and 5