oracle SQL 确定月中的连续周期

oracle SQL determine consecutive periods in months

早上好,

我每隔 1 个月就为我的数据仓库收集数据。 现在我想用 oracle sql.

确定几个月的连续周期是多少

如果这个月有中断,我想开始新的一期。

我的示例与下面的示例类似:

ID  LOAD_DATE  
100 20190101  
100 20190201  
100 20190401  
100 20190501  
100 20190601  
100 20190701  
100 20191001  
100 20191101  
100 20191201  
100 20200101  
200 20190701  
200 20190901  
200 20191101  
200 20191201  
200 20200101  
200 20200201  

期望的结果:

ID  From     To  
100 20190101 20190201  
100 20190401 20190701  
100 20191001 20200101  
200 20190701 20190701  
200 20190901 20190901  
200 20191101 20200201  

我可以管理基于一年的连续周期。由于年份的变化,我无法按月进行。

请帮忙。 我正在使用 Oracle sql 开发人员

实际支持的版本(即Oracle 12+):

select * 
from t
match_recognize(
  partition by id
  order by load_date
  measures
     first(LOAD_DATE) as ld,
     LAST(LOAD_DATE) AS lst,
     count(*) as cnt
  pattern (strt next*)
  define
     next as next.load_date=add_months(prev(load_date),1)
)
order by 1,2;

带有示例数据的完整示例:

alter session set nls_date_format='yyyymmdd';
with t (ID,LOAD_DATE) as (
   select 100, to_date('20190101') from dual union all  
   select 100, to_date('20190201') from dual union all  
   select 100, to_date('20190401') from dual union all  
   select 100, to_date('20190501') from dual union all  
   select 100, to_date('20190601') from dual union all  
   select 100, to_date('20190701') from dual union all  
   select 100, to_date('20191001') from dual union all  
   select 100, to_date('20191101') from dual union all  
   select 100, to_date('20191201') from dual union all  
   select 100, to_date('20200101') from dual union all  
   select 200, to_date('20190701') from dual union all  
   select 200, to_date('20190901') from dual union all  
   select 200, to_date('20191101') from dual union all  
   select 200, to_date('20191201') from dual union all  
   select 200, to_date('20200101') from dual union all  
   select 200, to_date('20200201') from dual
)
select * 
from t
match_recognize(
  partition by id
  order by load_date
  measures
     first(LOAD_DATE) as ld,
     LAST(LOAD_DATE) AS lst,
     count(*) as cnt
  pattern (strt next*)
  define
     next as next.load_date=add_months(prev(load_date),1)
)
order by 1,2;

我了解了一部分,正在处理剩余部分。

Demo

SELECT X.ID, X.LOAD_DATE, X.NEXT_MONTH FROM (
SELECT CASE 
WHEN TRUNC(ADD_MONTHS(TO_DATE(LOAD_DATE, 'YYYY-MM-DD'), 1)) = TRUNC(TO_DATE(NEXT_MONTH, 'YYYY-MM-DD')) 
THEN 1 ELSE 0 END AS CHECK_VALUE, A.* FROM
(
SELECT A.*, LEAD(LOAD_DATE) OVER(PARTITION BY ID ORDER BY LOAD_DATE) NEXT_MONTH
FROM TABLE1 A ) A) X WHERE CHECK_VALUE = 1;

一种解决方案使用 window 函数,将实际日期存储为数字,如下所示:

select id, min(load_date) "To", max(load_date) "From"
from (
  select id, load_date,
    sum(period_start) over (partition by id order by load_date_converted)
      period
  from (
    select id, load_date,
      to_date(load_date,'YYYYMMDD') load_date_converted,
      case when add_months(to_date(load_date,'YYYYMMDD'),-1)<>
        lag (to_date(load_date,'YYYYMMDD'),1,sysdate)
          over (partition by id order by load_date)
            then 1 else 0
      end period_start
    from table_name
  )
)
group by id, period
order by id, period

甲骨文版本:12c

最简单的方法是做一个简单的观察:如果您有一个数字序列来枚举每一行,那么当月份连续时,这个序列和加载日期之间的差异(以月为单位)是恒定的。然后,您可以按此差异进行汇总:

select id, min(load_date), max(load_date)
from (select t1.*, row_number() over (partition by id order by load_date) as seqnum
      from table1 t1
     ) t1
group by id, add_months(to_date(load_date, 'YYYYMMDD'), - seqnum)
order by id, min(load_date);

Here 是一个 db<>fiddle.