Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Como gerar linhas para intervalo de datas por chave


em 10g/11g você pode usar a cláusula model para isso.
SQL> with emps as (select rownum id, name, start_date,
  2                       end_date, trunc(end_date)-trunc(start_date) date_range
  3                  from table1)
  4  select name, the_date
  5    from emps
  6  model partition by(id as key)
  7        dimension by(0 as f)
  8        measures(name, start_date, cast(null as date) the_date, date_range)
  9        rules (the_date [for f from 0 to date_range[0] increment 1]  = start_date[0] + cv(f),
 10               name[any] = name[0]);

NAME        THE_DATE
----------- ----------
DAVID SMITH 01-01-2001
DAVID SMITH 01-02-2001
DAVID SMITH 01-03-2001
DAVID SMITH 01-04-2001
DAVID SMITH 01-05-2001
DAVID SMITH 01-06-2001
JOHN SMITH  02-07-2012
JOHN SMITH  02-08-2012
JOHN SMITH  02-09-2012

9 rows selected.

ou seja, sua consulta base:
select rownum id, name, start_date,
       end_date, trunc(end_date)-trunc(start_date) date_range
  from table1

apenas define as datas + o intervalo (usei o id rownum, mas se você tiver um PK, poderá usá-lo.

a partição divide nossos cálculos por ID (linha única):
6  model partition by(id as key)

as medidas:
8        measures(name, start_date, cast(null as date) the_date, date_range)

define os atributos que iremos gerar/calcular. neste caso, estamos trabalhando com name e start_date mais o intervalo de linhas a serem geradas. além disso, defini uma coluna the_date que conterá a data calculada (ou seja, queremos calcular start_date + n onde n é de 0 ao intervalo.

as regras definem COMO vamos preencher nossas colunas:
9        rules (the_date [for f from 0 to date_range[0] increment 1]  = start_date[0] + cv(f),
10               name[any] = name[0]);

então com
the_date [for f from 0 to date_range[0] increment 1]

estamos dizendo que vamos gerar o número de linhas que date_range contém +1 (ou seja, 6 datas no total). o valor de f pode ser referenciado através do cv função (valor atual).

então na linha 1 para david, teríamos the_date [0] = start_date+0 e, posteriormente, na linha 2, teríamos the_date [1] = start_date+1 . todo o caminho até start_date+5 (ou seja, a end_date )

p.s.para conectar por você precisaria fazer algo assim:
select 
    A.EMPLOYEE_NAME,
    A.START_DATE+(b.r-1) AS INDIVIDUAL_DAY,
    TO_CHAR(A.START_DATE,'MM/DD/YYYY') START_DATE,
    TO_CHAR(A.END_DATE,'MM/DD/YYYY') END_DATE
FROM table1 A
     cross join (select rownum r
                   from (select max(end_date-start_date) d from table1)
                  connect by level-1 <= d) b
 where A.START_DATE+(b.r-1) <= A.END_DATE
 order by 1, 2;

ou seja, isole o connect by a uma subconsulta e, em seguida, filtre as linhas onde individual_day> end_date.

mas eu NÃO recomendaria esta abordagem. seu desempenho será pior em comparação com a abordagem do modelo (especialmente se os intervalos forem grandes).