根据日期范围创建多行

Create multiple rows based off a date range

我有一个日历查询和下面的 table。我有一个成员的开始日期和结束日期。同样在我的日历 table 上,我根据开始日期捕获了一个 "Weekof"。我想记录某个成员是否在该周的任何时间处于活动状态。查看预期结果。

SELECT DISTINCT
  --CA.CALENDAR_DATE,
      TO_CHAR(CALENDAR_DATE,'MM/DD/YYYY') AS CALENDAR_DATE                                
           TO_CHAR(NEXT_DAY(CALENDAR_DATE, 'Monday') - 7, 'MM/DD/YY-') || 
      TO_CHAR(NEXT_DAY(CALENDAR_DATE, 'Monday') - 1, 'MM/DD/YY') AS WEEK_OF_YEAR,

      ROW_NUMBER () OVER ( ORDER BY CALENDAR_DATE) AS MasterCalendar_RNK

     FROM CALENDAR CA
     WHERE 1=1  
       --AND CA.CALENDAR_DATE BETWEEN ADD_MONTHS(TRUNC(SYSDATE), -12) AND TRUNC(SYSDATE)
       --AND CA.CALENDAR_DATE BETWEEN TRUNC(SYSDATE) -5 AND TRUNC(SYSDATE)
       ORDER BY TO_DATE(CALENDAR_DATE,'MM/DD/YYYY') DESC

Table

Member    StartDate    EndDate    
  A          1/31/17      
  B          2/1/17      2/15/17

预期输出:

Member    StartDate    EndDate    Week_Of_Year        Active
  A         1/31/17                  1/30/17-2/5/17      1
  A         1/31/17                  2/6/17-2/12/17      1
  A         1/31/17                  2/13/17-2/19/17     1
  B         2/1/17      2/15/17      1/30/17/2/5/17      1
  B         2/1/17      2/15/17      2/6/17-2/12/17      1
  B         2/1/17      2/15/17      2/13/17-2/19/17     1 

当前查询:

WITH MASTER_CALENDAR AS (
SELECT TRUNC(SYSDATE) + 1 - LEVEL , A.CALENDAR_DATE
FROM (SELECT C.CALENDAR_DATE FROM MST.CALENDAR C WHERE 1=1 AND C.CALENDAR_DATE > SYSDATE-30 AND C.CALENDAR_DATE < SYSDATE) A

WHERE 1=1

CONNECT BY LEVEL <= 1 --NEED TO UPDATE?

ORDER BY A.CALENDAR_DATE  DESC       
                         ),

ActiveMembers AS (
SELECT H.CLT_CLT_PGMID, H.START_DT

  ,CASE WHEN TRUNC(H.END_DT) = '1-JAN-3000' 
  THEN SYSDATE 
  ELSE TO_DATE(H.END_DT) 
  END AS END_DT

 FROM H
 WHERE 1=1 
  AND H.CLT_CLT_PGMID IN ('1','2','3')
                        )

SELECT CLT_CLT_PGMID, STARTDATE, ENDDATE, WEEK_OF_YEAR, ACTIVE -- but not week_start
FROM (
SELECT DISTINCT A.CLT_CLT_PGMID,
  TO_CHAR(A.START_DT, 'MM/DD/YY') AS STARTDATE,
  TO_CHAR(A.END_DT, 'MM/DD/YY') AS ENDDATE,
  NEXT_DAY(CAL.CALENDAR_DATE, 'Monday') - 7 AS WEEK_START, -- for ordering later
  TO_CHAR(NEXT_DAY(CAL.CALENDAR_DATE, 'Monday') - 7, 'MM/DD/YY-') || 
    TO_CHAR(NEXT_DAY(CAL.CALENDAR_DATE, 'Monday') - 1, 'MM/DD/YY') AS WEEK_OF_YEAR,
  1 AS ACTIVE

FROM ActiveMembers A
  INNER JOIN MASTER_CALENDAR CAL ON CAL.CALENDAR_DATE BETWEEN A.START_DT AND A.END_DT
                                                                     --BETWEEN TO_CHAR(A.START_DT,'MM/DD/YYYY') AND COALESCE(A.END_DT,(SYSDATE))                                                                  
                                      ) 
WHERE 1=1

ORDER BY 
CLT_CLT_PGMID , STARTDATE, ENDDATE, WEEK_START
                    ;

由于日历查询当前会生成字符串,因此返回日历 table、将其加入您的 member/date table 并重新生成周范围字符串会更简单:

用 CTE 来表示您的日历 table(目前只有最近几周的日期)和会员数据:

with calendar(calendar_date) as (
  select trunc(sysdate) + 1 - level from dual connect by level <= 42
),
mytable (member, startdate, enddate) as (
  select cast('A' as varchar2(6)), date '2017-01-31', cast (null as date) from dual
  union all select cast('B' as varchar2(6)), date '2017-02-01', date '2017-02-15' from dual
)
select member, startdate, enddate, week_of_year, active -- but not week_start
from (
    select distinct m.member,
      to_char(m.startdate, 'MM/DD/YY') as startdate,
      to_char(m.enddate, 'MM/DD/YY') as enddate,
      next_day(c.calendar_date, 'Monday') - 7 as week_start, -- for ordering later
      to_char(next_day(c.calendar_date, 'Monday') - 7, 'MM/DD/YY-') || 
        to_char(next_day(c.calendar_date, 'Monday') - 1, 'MM/DD/YY') as week_of_year,
      1 as active
    from mytable m
    join calendar c
    on c.calendar_date between m.startdate and coalesce(m.enddate, trunc(sysdate))
)
order by member, startdate, enddate, week_start;

获得

MEMBER STARTDAT ENDDATE  WEEK_OF_YEAR          ACTIVE
------ -------- -------- ----------------- ----------
A      01/31/17          01/30/17-02/05/17          1
A      01/31/17          02/06/17-02/12/17          1
A      01/31/17          02/13/17-02/19/17          1
A      01/31/17          02/20/17-02/26/17          1
B      02/01/17 02/15/17 01/30/17-02/05/17          1
B      02/01/17 02/15/17 02/06/17-02/12/17          1
B      02/01/17 02/15/17 02/13/17-02/19/17          1

您没有为没有结束日期的成员指定上限,所以我今天通过 coalesce() 使用了。

内部查询仅在订购时需要,因为不能使用周范围字符串,并且您不想看到周自己开始;并且您不能按您未选择的字段使用 distinct 和 order。

我会以与 Alex 类似的方式来执行此操作,但略有不同。鉴于您的周从星期一开始,我将使用 TRUNC(dt, 'iw') 获取指定日期的一周的 ISO 开始(恰好定义为星期一)。然后我会在加入你的table之前得到那些不同的值,就像这样:

with calendar as (select trunc(sysdate) - level + 1 calendar_date
                  from   dual
                  connect by level <= 50),
   your_table as (select 'A' member, date '2017-01-31' startdate, NULL enddate from dual union all
                  select 'B' member, date '2017-02-01' startdate, date '2017-02-15' enddate from dual)
select yt.member,
       yt.startdate,
       yt.enddate,
       to_char(c.week_start, 'mm/dd/yyyy')
         || ' - ' || to_char(c.week_start + 6, 'mm/dd/yyyy') week_of_year,
       1 as active
from   your_table yt
       inner join (select distinct trunc(cl.calendar_date, 'iw') week_start
                   from   calendar cl) c on c.week_start <= nvl(yt.enddate, SYSDATE) AND c.week_start + 6 >= yt.startdate
order by yt.member,
         c.week_start;
MEMBER STARTDATE  ENDDATE    WEEK_OF_YEAR                ACTIVE
------ ---------- ---------- ----------------------- ----------
A      01/31/2017            01/30/2017 - 02/05/2017          1
A      01/31/2017            02/06/2017 - 02/12/2017          1
A      01/31/2017            02/13/2017 - 02/19/2017          1
A      01/31/2017            02/20/2017 - 02/26/2017          1
B      02/01/2017 02/15/2017 01/30/2017 - 02/05/2017          1
B      02/01/2017 02/15/2017 02/06/2017 - 02/12/2017          1
B      02/01/2017 02/15/2017 02/13/2017 - 02/19/2017          1

和 Alex 一样,我假设您的结束日期 运行 为空,直到今天 (sysdate)。但是,查看成员 B 的结果,您似乎在寻找重叠范围(因为 1 月 30 日不在 2 月 1 日和 15 日之间),所以我相应地修改了我的连接子句。这会导致成员 A 多一行,所以也许您想要 运行 空结束日期直到 sysdate 的前一个星期日?不确定。如果需要,我相信您可以自己修改。