【问题标题】:Grouping SQL results by continuous time intervals (oracle sql)按连续时间间隔对 SQL 结果进行分组(oracle sql)
【发布时间】:2010-09-30 10:59:53
【问题描述】:

您好,我在表格中有以下数据:

ID-----startDate----endDate
5549 2008-05-01 4712-12-31
5567 2008-04-17 2008-04-30 1
5567 2008-05-01 2008-07-31 1
5567 2008-09-01 4712-12-31 2

5569 2008-05-01 2008-08-31
5569 2008-09-01 4712-12-31
5589 2008-04-18 2008-04-30
5589 2008-05-01 4712-12-31
5667 2008-05-01 4712-12-31
5828 2008-06-03 4712-12-31
5867 2008-06-03 4712-12-31
6167 2008-11-01 4712-12-31
6207 2008-07-01 4712-12-31
6228 2008-07-01 4712-12-31
6267 2008-07-14 4712-12-31

我正在寻找将每个 id 返回的连续时间间隔分组的方法:

身份证, 分钟(开始日期), 最大值(结束日期),

在粗体 ID 5567 的结果中得到类似的结果

5567 2008-04-17 2008-07-31
5567 2008-09-01 4712-12-31

PL/SQL 也是这里的一个选项 :)

谢谢,

【问题讨论】:

    标签: sql oracle date continuous intervals


    【解决方案1】:

    我认为这将满足您的需求: (请注意,重叠范围可能会混淆;不知道它们是否可能在您的数据集中)

    select id, min(start_date) period_start, max(end_date) period_end
    from
    (
    select 
        id, start_date, end_date,
        max(contig) over (partition by id order by end_date) contiguous_group
    from
    (
    select 
        id, start_date, end_date,
        case 
            when lag(end_date) over (partition by id order by end_date) != start_date-1 or row_number() over (partition by id order by end_date)=1 
                then row_number() over (partition by id order by end_date) else null end contig
    from t2
    )
    )
    group by id, contiguous_group
    order by id, period_start
    /
    

    这是我使用的测试数据 - 基于您的数据,还有一些额外的条目:

    create table t2 (id number, start_date date, end_date date);
    
    insert into t2(id, start_date, end_date)values(5549, to_date('2008-05-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5567, to_date('2008-04-17', 'yyyy-mm-dd'), to_date('2008-04-30', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5567, to_date('2008-05-01', 'yyyy-mm-dd'), to_date('2008-07-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5567, to_date('2008-08-01', 'yyyy-mm-dd'), to_date('2008-08-14', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5567, to_date('2009-09-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5567, to_date('2008-11-17', 'yyyy-mm-dd'), to_date('2008-12-13', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5567, to_date('2008-12-14', 'yyyy-mm-dd'), to_date('2008-12-24', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5569, to_date('2008-05-01', 'yyyy-mm-dd'), to_date('2008-08-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5569, to_date('2008-09-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5589, to_date('2008-04-18', 'yyyy-mm-dd'), to_date('2008-04-30', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5589, to_date('2008-05-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5667, to_date('2008-05-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5828, to_date('2008-06-03', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(5867, to_date('2008-06-03', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(6167, to_date('2008-11-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(6207, to_date('2008-07-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(6228, to_date('2008-07-01', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    insert into t2(id, start_date, end_date)values(6267, to_date('2008-07-14', 'yyyy-mm-dd'), to_date('4712-12-31', 'yyyy-mm-dd'));
    
    commit;
    

    【讨论】:

      【解决方案2】:

      你可以用这样的分析函数来做到这一点:

      with d as
      ( select id, start_date, end_date
        ,      case when start_date = prev_end+1 
                    then 'cont' else 'new' end start_status
        ,      case when end_date = next_start-1
                    then 'cont' else 'new' end end_stat
        from
        (
        select id, start_date, end_date
        ,      lag(end_date) over (partition by id order by start_date) prev_end
        ,      lead(start_date) over (partition by id order by start_date) next_start
        from t1
        order by id, start_date
        )
      )
      select starts.id, starts.start_date, ends.end_date
      from
      ( select id, start_date, row_number() over (order by id, start_date) rn
        from   d
        where  start_status='new'
      ) starts,
      ( select id, end_date, row_number() over (order by id, start_date) rn
        from   d
        where  end_status='new'
      ) ends
      where starts.rn = ends.rn
      

      我用你的数据得到这个结果:

              ID START_DATE END_DATE
      ---------- ---------- ----------
            5549 2008-05-01 4712-12-31
            5567 2008-04-17 2008-07-31
            5567 2008-09-01 4712-12-31
            5569 2008-05-01 4712-12-31
            5589 2008-04-18 4712-12-31
            5667 2008-05-01 4712-12-31
            5828 2008-06-03 4712-12-31
            5867 2008-06-03 4712-12-31
            6167 2008-11-01 4712-12-31
            6207 2008-07-01 4712-12-31
            6228 2008-07-01 4712-12-31
            6267 2008-07-14 4712-12-31
      
      12 rows selected.
      

      它是如何工作的:

      1. WITH 子句生成数据的视图 D,其中每一行都分配了一个“开始状态”和一个“结束状态”,每行都是“新”或“继续”以指示它是否与前一个连续/下一行与否。
      2. 内嵌视图“开始”和“结束”仅拉出分别具有“新”开始状态/结束状态的行,并使用行号将它们结合起来。
      3. “主查询”然后从这 2 个视图中进行选择,并在行号列上进行连接。

      【讨论】:

        【解决方案3】:

        您必须编写如下所示的 PL/SQL 块示例逻辑;

        Create or Replace someproc
        Declare
            Cursore someCur AS
            Select * from someTable
            Order by ID,StartDate
        
            IDVar as Varchar(10)
            MinDate as DATE
            MaxDate as DATE
        
        Begin
            Open someCur
            Fetch ID,StartDate,EndDate into IDVar,MinDate,MaxDate
            While SomeCur%NOTFOUND
            LOOP
                Fetch ID,StartDate,EndDate into TempID,TempStartDate,TempEndDate
                if IDVar <> TempID then
                    -- output into your required structure values: IDVar,MinDate,MaxDate
                    IDVar = TempID
                    MinDate = TempStartDate
                    MaxDate = TempEndDate
                    Exit Loop
                ELSE IF
                    MaxDate+1 >= TempStartDate THEN
                    MaxDate = TempEndDate
                END IF
            End LOOP
        

        【讨论】:

          【解决方案4】:

          我不在要测试的实例附近,但是您尝试过吗;

          SELECT
           ID, 
           startDate,
           endDate
          FROM
           myTable
          WHERE
           (ID, startDate) in 
           (SELECT
             ID, 
             min(startDate) 
            FROM
             myTable
            GROUP BY
             ID
            )
          
            or 
          
           (ID, endDate) in 
           (SELECT
             ID, 
             max(endDate) 
            FROM
             myTable
            GROUP BY
             ID
            )
          

          这应该为您提供所有最早的startDates,以及每个 ID 的最新endDates。连续与否。

          【讨论】:

            【解决方案5】:
            WITH
              laik_test AS -- sample data
            (select 1001 id, date'2012-01-03' start_date, date'2012-06-29' end_date from dual union
             select 1001 id, date'2012-03-03' start_date, date'2012-08-29' end_date from dual union
             select 1002 id, date'2012-06-03' start_date, date'2012-11-29' end_date from dual union
             select 1001 id, date'2012-09-03' start_date, date'2013-02-20' end_date from dual union
             select 1001 id, date'2013-02-08' start_date, date'2013-04-29' end_date from dual union
             select 1002 id, date'2012-11-03' start_date, date'2012-12-29' end_date from dual union
             select 1002 id, date'2012-12-23' start_date, date'2013-09-29' end_date from dual union
             select 1002 id, date'2013-08-03' start_date, date'2015-06-29' end_date from dual union
             select 1001 id, date'2013-04-13' start_date, date'2013-09-29' end_date from dual union
             select 1001 id, date'2013-07-03' start_date, date'2014-06-29' end_date from dual union
             select 1003 id, date'2012-12-23' start_date, date'2013-09-29' end_date from dual union
             select 1001 id, date'2013-07-03' start_date, date'2014-06-29' end_date from dual union
             select 1003 id, date'2012-12-23' start_date, date'2013-09-29' end_date from dual union
             select 1003 id, date'2013-09-30' start_date, date'2014-06-29' end_date from dual union
             select 1003 id, date'2013-12-30' start_date, date'2014-03-11' end_date from dual union
             select 1003 id, date'2014-06-29' start_date, date'2015-09-29' end_date from dual )
            , matrica AS
            (  select id, start_date, end_date
                    , lead(start_date) over (partition by id order by start_date, end_date) start_date_next
                    , lag(end_date) over (partition by id order by start_date, end_date) end_date_prev
              from laik_test m
             where not exists (select * 
                                 from laik_test n
                                where m.id = n.id
                                  and m.start_date > n.start_date and m.end_date < n.end_date))
            , matrica2 AS
            (select id, end_date
              from matrica m
             where start_date_next is null OR start_date_next > end_date + 1)
            , matrica3 AS
            (select id, start_date
              from matrica m
             where end_date_prev is null OR end_date_prev < start_date - 1)
            , matrica4 AS
            (select m2.id, m3.start_date, m2.end_date
              from matrica2 m2, matrica3 m3
             where m2.id=m3.id and m3.start_date < m2.end_date)
            select id, start_date, end_date
              from matrica4 m
             where not exists (select * from matrica4 n
                                where m.id = n.id
                                  and (   (n.start_date = m.start_date and m.end_date > n.end_date)
                                       OR (n.end_date = m.end_date and m.start_date < n.start_date)
                                      ));
            

            结果是:

            ID   | start_date | end_date
            1001 | 2012-01-03 | 2012-08-29
            1001 | 2012-09-03 | 2014-06-29
            1002 | 2012-06-03 | 2015-06-29
            1003 | 2012-12-23 | 2015-09-29
            

            【讨论】:

              【解决方案6】:

              我一直在尝试为重叠范围做一些事情,但遇到了一些困难 使用这些数据:

                   INSERT INTO zzz_scrap_dates (id,effdate,termdate)
              
              SELECT id,effdate,termdate from (
              SELECT '1'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '1'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2010-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '1'id ,To_Date('2005-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '1'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '1'id ,To_Date('1999-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              union
              SELECT '2'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '2'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2010-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '2'id ,To_Date('2005-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '2'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '2'id ,To_Date('1999-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              union
              SELECT '3'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '3'id ,To_Date('1998-01-01','YYYY-MM-DD')effdate,To_Date('1999-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '3'id ,To_Date('1005-01-01','YYYY-MM-DD')effdate,To_Date('1197-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '3'id ,To_Date('2000-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              UNION
              SELECT '3'id ,To_Date('1197-01-01','YYYY-MM-DD')effdate,To_Date('2020-01-31','YYYY-MM-DD')termdate FROM dual
              

              【讨论】:

              • 我建议您解释一下您的代码。这是SELECTUNION 声明的墙。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-09-21
              • 1970-01-01
              • 2016-04-02
              • 2016-04-15
              相关资源
              最近更新 更多