2012-11-06 17 views
0

不確定從何處開始查詢,但我有兩個表TEST_A和TEST_B。Oracle查詢填寫分組記錄之間的日差

TEST_B包含用於特定ID的特定日期範圍內,而TEST_A包含用於與所提供的分配值的ID的日期範圍。

下面是創建和填充表的DDL。

CREATE TABLE TEST_A 
(
    ID   VARCHAR2(5), 
    START_DATE DATE, 
    END_DATE DATE, 
    ASSIGNMENT VARCHAR2(25) 
) 
STORAGE (
      BUFFER_POOL  DEFAULT 
      ) 
LOGGING 
NOCOMPRESS 
NOCACHE 
NOPARALLEL 
NOMONITORING; 


CREATE TABLE TEST_B 
(
    ID   VARCHAR2(5), 
    START_DATE DATE, 
    END_DATE DATE 
) 
STORAGE (
      BUFFER_POOL  DEFAULT 
      ) 
LOGGING 
NOCOMPRESS 
NOCACHE 
NOPARALLEL 
NOMONITORING; 

和腳本來填充表:

INSERT INTO TEST_A(ID, START_DATE, END_DATE, ASSIGNMENT) 
VALUES('A', TO_DATE('01/01/2010', 'MM/DD/YYYY'), TO_DATE('01/31/2010', 'MM/DD/YYYY'), 'Lot A'); 

INSERT INTO TEST_A(ID, START_DATE, END_DATE, ASSIGNMENT) 
VALUES('A', TO_DATE('02/01/2010', 'MM/DD/YYYY'), TO_DATE('02/15/2010', 'MM/DD/YYYY'), 'Lot A'); 

INSERT INTO TEST_A(ID, START_DATE, END_DATE, ASSIGNMENT) 
VALUES('A', TO_DATE('02/18/2010', 'MM/DD/YYYY'), TO_DATE('02/28/2010', 'MM/DD/YYYY'), 'Lot C'); 

INSERT INTO TEST_A(ID, START_DATE, END_DATE, ASSIGNMENT) 
VALUES('A', TO_DATE('03/01/2010', 'MM/DD/YYYY'), TO_DATE('03/31/2010', 'MM/DD/YYYY'), 'Lot D'); 

INSERT INTO TEST_A(ID, START_DATE, END_DATE, ASSIGNMENT) 
VALUES('B', TO_DATE('08/01/2010', 'MM/DD/YYYY'), TO_DATE('08/31/2010', 'MM/DD/YYYY'), 'Lot E'); 

INSERT INTO TEST_A(ID, START_DATE, END_DATE, ASSIGNMENT) 
VALUES('B', TO_DATE('09/15/2010', 'MM/DD/YYYY'), TO_DATE('09/30/2010', 'MM/DD/YYYY'), 'Lot E'); 

INSERT INTO TEST_A(ID, START_DATE, END_DATE, ASSIGNMENT) 
VALUES('C', TO_DATE('09/15/2010', 'MM/DD/YYYY'), TO_DATE('09/30/2010', 'MM/DD/YYYY'), 'Lot E'); 


INSERT INTO TEST_B(ID, START_DATE, END_DATE) 
VALUES('A', TO_DATE('01/01/2010', 'MM/DD/YYYY'), TO_DATE('12/31/2099', 'MM/DD/YYYY')); 

INSERT INTO TEST_B(ID, START_DATE, END_DATE) 
VALUES('B', TO_DATE('08/01/2010', 'MM/DD/YYYY'), TO_DATE('12/31/2099', 'MM/DD/YYYY')); 

INSERT INTO TEST_B(ID, START_DATE, END_DATE) 
VALUES('C', TO_DATE('01/01/2010', 'MM/DD/YYYY'), TO_DATE('12/31/2099', 'MM/DD/YYYY')); 

從數據上看,我需要一羣來自TEST_A這些記錄被分配,並在兩者之間填充缺失的一天的差距。每個ID的記錄還應涵蓋表TEST_B中提供的整個開始和結束日期。爲了進一步解釋,得出的數據,我需要是這樣的:

ID START_DATE END_DATE  ASSIGNMENT 
A 01/01/2010 02/15/2010 Lot A 
A 02/16/2010 02/17/2010 {NULL} 
A 03/01/2010 03/31/2010 Lot D 
A 04/01/2010 12/31/2099 {NULL} 
B 08/01/2010 08/31/2010 Lot E 
B 09/01/2010 09/14/2010 {NULL} 
B 09/15/2010 09/30/2010 Lot E 
B 10/01/2010 12/31/2099 {NULL} 
C 01/01/2010 09/14/2010 {NULL} 
C 09/15/2010 09/30/2010 Lot E 
C 10/01/2010 12/31/2099 {NULL} 

欣賞構建查詢這方面的任何幫助。 LAG功能在腦海中浮現,但我不確定如何正確寫下它。謝謝。

增加: 如果存儲過程將在處理這提供了靈活性,那麼這仍然是一個接受的解決方案。

+0

對於給定的ID,以後可以有很多重複的?例如,在ID = A可以從2010年4月重新出現「lot A」嗎?或地段A的日期將永遠是連續的? – DazzaL

+0

對於每個給定的ID,有可能重複分配。 – Angelo

+0

重複是好的,我的意思是如果我們添加這種複雜性: INSERT INTO TEST_A(ID,日期,結束日期,ASSIGNMENT) VALUES( 'A',TO_DATE('04/01/2010' , 'MM/DD/YYYY' ),TO_DATE('05/30/2010','MM/DD/YYYY'),'批次A'); 是有效的(所以在你的最終答案中,我們有兩個ID = A + ASSIGNMENT =批次A行 – DazzaL

回答

1

一個流水線函數方法(爲了顯示結果,在這裏增加了另一個批次A(id = A)行)。

create type test_typ as object(id varchar2(5), 
           start_date date, 
           end_date date, 
           assignment varchar2(25) 
          ); 
/
create type test_tab as table of test_typ; 
/

create or replace function test_pipe 
return test_tab pipelined is 

begin 

    for r_row in (select a.id, a.start_date, a.end_date, 
         a.assignment, 
         lead(a.start_date, 1) over (partition by a.id order by a.start_date) next_start_date, 
         lead(a.id, 1) over (order by a.id, a.start_date) next_id, 
         lag(a.id, 1) over (order by a.id, a.start_date) prior_id, 
         b.start_date min_start, b.end_date max_end 
        from (select id, min(start_date) start_date, max(end_date) end_date, assignment 
          from (select id, start_date, end_date, assignment, rn, max(rn) over (partition by id order by start_date) r 
            from (select id, start_date, end_date, assignment, 
             case 
             when lag(end_date, 1) over (partition by id, assignment order by start_date) = start_date-1 
             then null 
             else row_number() over (order by start_date) 
             end rn 
            from test_a) 
           order by id, start_date) 
         group by id, assignment, r) a, 
         test_b b 
       where b.id = a.id 
       order by id, start_date) 
    loop 
    if ((r_row.prior_id != r_row.id or r_row.prior_id is null) and r_row.start_date > r_row.min_start) 
    then 
     pipe row(test_typ(r_row.id, r_row.min_start, r_row.start_date-1, null)); 
    end if; 
    pipe row(test_typ(r_row.id, r_row.start_date, r_row.end_date, r_row.assignment)); 
    if (r_row.next_start_date != r_row.end_date + 1) 
    then 
     pipe row(test_typ(r_row.id, r_row.end_date + 1, r_row.next_start_date-1, null)); 
    elsif ((r_row.next_id != r_row.id or r_row.next_id is null) and r_row.end_date < r_row.max_end) 
    then 
     pipe row(test_typ(r_row.id, r_row.end_date + 1, r_row.max_end, null)); 
    end if; 
    end loop; 
end test_pipe; 

數據:

SQL> select * from test_a order by 1, 2; 

ID START_DAT END_DATE ASSIGNMENT 
----- --------- --------- ------------------------- 
A  01-JAN-10 31-JAN-10 Lot A 
A  01-FEB-10 15-FEB-10 Lot A 
A  18-FEB-10 28-FEB-10 Lot C 
A  01-MAR-10 31-MAR-10 Lot D 
A  01-APR-10 30-MAY-10 Lot A 
B  01-AUG-10 31-AUG-10 Lot E 
B  15-SEP-10 30-SEP-10 Lot E 
C  15-SEP-10 30-SEP-10 Lot E 


SQL> select * 
    2 from table(test_pipe()) b; 

ID START_DAT END_DATE ASSIGNMENT 
----- --------- --------- ------------------------- 
A  01-JAN-10 15-FEB-10 Lot A 
A  16-FEB-10 17-FEB-10 
A  18-FEB-10 28-FEB-10 Lot C 
A  01-MAR-10 31-MAR-10 Lot D 
A  01-APR-10 30-MAY-10 Lot A 
A  31-MAY-10 31-DEC-99 
B  01-AUG-10 31-AUG-10 Lot E 
B  01-SEP-10 14-SEP-10 
B  15-SEP-10 30-SEP-10 Lot E 
B  01-OCT-10 31-DEC-99 
C  01-JAN-10 14-SEP-10 
C  15-SEP-10 30-SEP-10 Lot E 
C  01-OCT-10 31-DEC-99 

13 rows selected. 

SQL> 
+0

哇。這真是太棒了。謝謝! – Angelo

+0

最初沒有想到這個,但是如果TEST_B表中有多條記錄會怎麼樣呢?這似乎破壞了查詢。插入語句以添加此方案。 – Angelo