【问题标题】:Sql Server: Any way to group records by date based on the dates of other records?Sql Server:有什么方法可以根据其他记录的日期按日期对记录进行分组?
【发布时间】:2017-07-14 19:08:15
【问题描述】:

我有一个包含事件的表,我需要查找重复的事件。问题是在 1 秒内发生的事件被认为是重复的。所以如果我的表有这些值

id | var1 | var2 | var3 | date
1  | 1    | 2    | 3    | 2001-01-01 01:01:01.456
2  | 1    | 2    | 3    | 2001-01-01 01:01:02.234
3  | 1    | 2    | 3    | 2001-01-01 01:01:04.789

记录 1 和 2 被认为是重复的,因为它们在一秒之内,但 3 不是因为它在 2 之后超过一秒。

有没有办法编写一个只选择一系列重复项中的第一条记录的查询?

编辑:可能还有一些不重复的行需要被捕获。 id是表的主键,不用于匹配条件;它只是为了澄清。

【问题讨论】:

  • 如果一秒钟内有三条记录怎么办……只保留一条?并且 ID 只是一个 PK,这意味着应该考虑 var1、var2 和 var3 是否重复?即如果它们不一样,那么它不是重复的?
  • @scsimon 是的。 var1、2、3 相同且日期在 1 秒以内的所有记录,只保留 1 条记录。该范围内是否有 2 或 200 无关紧要。 Id 是一个 PK。
  • 感谢@RossD 的澄清。我刚刚编辑了我的答案,我 thin 它应该适用于所有情况。如果没有,请告诉我。为了清楚起见,我把它放在了多个 cte 中。

标签: sql sql-server tsql sql-server-2014


【解决方案1】:

这是一种看起来应该适合您的方法。

一些假设:

  1. 根据您提供的 1 second 子句,我假设重复是不包括 ID 的实际行重复。如果不是这种情况...通过row_number() 窗口函数的一部分删除分区,它将改变行为
  2. 这将删除递归重复。也就是说,如果 3,4 甚至 15 行彼此在一秒之内,则保持 1。
  3. 无论第一行或最后一行是否重复,这都应该有效

这是代码。取消注释表中的两行以查看更改

declare @table table(id int, var1 int, var2 int, var3 int, date datetime2)
insert into @table
values
--(0,1,2,3,'2001-01-01 00:01:01.456'), 

(1,1,2,3,'2001-01-01 01:01:01.456'), --dupe of 1/2/3
(2,1,2,3,'2001-01-01 01:01:02.214'), --dupe of 1/2/3
(3,1,2,3,'2001-01-01 01:01:02.234'), --dupe of 1/2/3
(4,1,2,3,'2001-01-01 01:01:02.244'), --dupe of 1/2/3

(5,1,2,3,'2001-01-01 01:01:04.789'), --dupe of 4/5
(6,1,2,3,'2001-01-01 01:01:04.989'), --dupe of 4/5

--(7,1,2,3,'2001-01-01 01:01:06.789'), --dupe of 6/7
(8,1,2,3,'2001-01-01 01:01:06.799') --dupe of 6/7

--apply the sequence
;with cte as(
select 
    *,
    ROW_NUMBER() over (partition by var1, var2, var3 order by date) as RN  --just in case... change this to just order by id, date if need be and remove the partition
from 
    @table),

--get first / most of the batch to remove
cte2 as(
select
    c1.*
    ,c2.RN as RowsToRemove
from cte c1
left join
    cte c2 on c1.RN < c2.rn  and 
    datediff(second,c1.date,c2.date) < 1),


--remove the rows identified in the above cte
cte3 as(
select distinct
    ID, 
    var1,
    var2,
    var3,
    date,
    RN
from cte2
where 
    RN not in (select distinct isnull(RowsToRemove,0) from cte2)),

--add another sequence. This is necessary for first/last row check for duplicate
cte4 as(
select
    f.*,
    row_number() over (partition by var1, var2, var3 order by date) RN2
from 
    cte3 f)

--return the results
select 
    f.ID, 
    f.var1,
    f.var2,
    f.var3,
    f.date
from 
    cte4 f
left join
    cte4 d on d.RN = f.RN - 1
where isnull(datediff(second,d.date,f.date),500) > 1

返回

+----+------+------+------+-----------------------------+
| ID | var1 | var2 | var3 |            date             |
+----+------+------+------+-----------------------------+
|  1 |    1 |    2 |    3 | 2001-01-01 01:01:01.4560000 |
|  5 |    1 |    2 |    3 | 2001-01-01 01:01:04.7890000 |
|  8 |    1 |    2 |    3 | 2001-01-01 01:01:06.7990000 |
+----+------+------+------+-----------------------------+

【讨论】:

    【解决方案2】:

    滞后是一种可能的解决方案,如下所示:

    select * from (
    select *, lag(date,1) over(order by date) previoustime from yourtable
    ) x
    where datediff(second,previoustime,date)<1
    

    【讨论】:

      【解决方案3】:
          select T1.date,... from MyTable T1
      left outer join MyTable T2 on cast(T1.date as date) = cast(T2.date as date) and 
      datediff(second,T1.date,T2.date)<=1
      group by cast(T1.date as date)
      

      【讨论】:

        猜你喜欢
        • 2021-12-05
        • 2015-12-06
        • 1970-01-01
        • 2020-05-23
        • 1970-01-01
        • 2023-03-17
        • 2014-01-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多