Oracle Query填补分组记录之间的日间差距

时间:2012-11-06 16:06:27

标签: oracle plsql

不确定从哪里开始查询,但我有两个表,TEST_A和TEST_B。

TEST_B包含特定ID的特定日期范围,而TEST_A包含具有提供的ASSIGNMENT值的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功能,但我不确定如何正确地将其写下来。感谢。

增加: 如果存储过程将提供处理这个的灵活性,那么这仍然是一个可接受的解决方案。

1 个答案:

答案 0 :(得分:1)

一个流水线函数方法(我在这里添加了另一个Lot 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>