2017-02-23 202 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?不確定。如果需要,我相信你可以自己修改。