2017-02-23 162 views
1

我有一个日历查询和下面的表格。我有一个成员的StartDate和结束日期。同样在我的日历表上,我基于startDate捕获了一个“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 

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 
        ; 

回答

1

由于日历查询当前生成的字符串,它会更简单回去到日历表,joi n表示对您的会员/日期表,并重新生成周范围字符串:

由于热膨胀系数来表示您的日历表(只是日期的最后几个星期现在)和成员的数据:

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()

内部查询仅用于排序,因为无法使用周范围字符串,并且您不希望自己查看周开始;而且你不能使用不同的字段来排序,而你没有选择字段。

+0

我只需要日历的最后30天,所以我添加了结果的子查询。我不熟悉连接,但我改变了连接级别从<= 42到<= 1我不知道这是什么。你能解释一下吗?这个查询适用于我的例子,但不明白这个例子。 – John

+0

我的当前查询根据您的答案显示在我的问题下。 – John

+0

@John - 42限制连接子查询返回的行数。与这个数字没有特别的关系,我只是选择了一个能够为您的示例数据生成至少足够日期的数据。如果你自己运行子查询,你会发现它会生成一个单独的日期范围。如果将42更改为30,它将生成最后30天而不是最后42天(将其更改为1将仅显示1日期)。但是如果您从日历表中获取日期,则根本不需要连接。 (我说CTEs是代表你的真实数据。) –

1

我会以类似亚历克斯的方式做到这一点,但略有不同。看到你的星期从星期一开始,我会用TRUNC(dt, 'iw')来获得指定日期的ISO开始的一周(恰好定义为星期一)。然后,我会得到这些的不同值加入到你的表,像这样前:

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 

像亚历克斯,我认为你的空结束日期运行,直到今天(SYSDATE)。然而,看看你的成员B的结果,看起来你正在寻找一个重叠的范围(自1月30日不在1月1日到15日之间),所以我已经修改了我的连接条款。这会导致成员A有额外的行,所以也许你想在sysdate的前一个星期天运行null enddates?不确定。如果需要,我相信你可以自己修改。