按日期对错误阈值进行分组

时间:2013-12-17 16:32:05

标签: sql oracle

我有很多日期/时间的项目。

  

[自动增量],[last_modified_date]
  item1,2013-sep-01 11:01:01
  item2,2013-sep-01 11:01:02
  item3,2013-sep-01 11:10:04
  item4,2013-sep-01 11:10:05
  item5,2013-sep-01 11:10:06
  item6,2013-sep-02 10:10:01
  item7,2013-sep-02 10:10:01

我需要按日期对项目进行分组,但分组必须支持几秒钟的错误阈值(比方说4秒)。我想把这作为新的结果。

  

[自动增量],[last_modified_date]
  group1,2013-sep-01 11:01:01
  group2,2013-sep-01 11:10:04
  group3,2013-sep-02 10:10:01

     

item1,2013-sep-01 11:01:01,group1
  item2,2013-sep-01 11:01:02,group1
  item3,2013-sep-01 11:10:04,group2
  item4,2013-sep-01 11:10:05,group2
  item5,2013-sep-01 11:10:06,group2
  item6,2013-sep-02 10:10:01,group3
  item7,2013-sep-02 10:10:01,group3

我可以通过循环所有项目来轻松完成,检查组是否存在,如果不存在则添加新组。
从abs((group.last_modified_date-item.last_modified_date)* 24 * 60 * 60)<组中选择coun(*)。 4

但是循环很慢。有没有办法通过单个查询将日期插入到组表中?子查询很好,我的目标是删除循环。

通过last_modified_date从项目组中选择last_modified_date,但也将彼此靠近的日期分组。

2 个答案:

答案 0 :(得分:3)

您可以使用的一个技巧是使用分析函数将记录分配给组。您可以将lagcase结合使用来标记组中的第一个“成员” - 即前一行的日期超过当前行日期之前的N秒(我是这里选择N = 5)。然后,新组的所有开始时间都是标记的记录:

with groups as (
  select
    auto_increment,
    last_modified_date,
    case when last_modified_date -
      lag(last_modified_date, 1, date '1900-01-01')
        over (order by last_modified_date) < (1 / 24 / 60 * 5) then 1 else 0
    end as starts_new_group
  from your_table
)
select
  auto_increment,
  last_modified_date
where
  starts_new_group = 1;

NB我没有测试这段代码!如果你可以用数据创建一个SQL小提琴,我可以在必要时调试查询。

答案 1 :(得分:0)

您可以通过分组的cte创建驱动程序表,然后JOIN使用BETWEEN创建一个驱动程序表。我不知道oracle语法,但这适用于SQL Server,所以希望你可以适应它:

DECLARE @begindate DATETIME = (SELECT MIN(last_modified_date) FROM #Table1)
       ,@enddate DATETIME = (SELECT MAX(last_modified_date) FROM #Table1)
;WITH cte AS (SELECT @begindate AS group_beg
                    ,DATEADD(second,3,@begindate) AS group_end
                    , 1 AS Group_
              UNION  ALL
              SELECT DATEADD(second,4,group_beg)
                   , DATEADD(second,7,group_beg)
                   , Group_ + 1
              FROM cte
              WHERE group_beg < @enddate
             )
SELECT a.*,DENSE_RANK() OVER(ORDER BY b.Group_)
FROM #Table1 a
JOIN cte b
  ON a.last_modified_date BETWEEN b.group_beg AND b.group_end
OPTION (MAXRECURSION 0)