Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment générer des lignes pour la plage de dates par clé

en 10g/11g, vous pouvez utiliser la clause modèle pour cela.

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.

c'est-à-dire votre requête de base :

select rownum id, name, start_date,
       end_date, trunc(end_date)-trunc(start_date) date_range
  from table1

définit simplement les dates + la plage (j'ai utilisé l'identifiant rownum, mais si vous avez un PK, vous pouvez l'utiliser à la place.

la partition divise nos calculs par ID (ligne unique) :

6  model partition by(id as key)

les mesures :

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

définit les attributs que nous allons générer/calculer. dans ce cas, nous travaillons avec le nom et le start_date plus la plage de lignes à générer. en plus j'ai défini une colonne the_date qui contiendra la date calculée (c'est-à-dire que nous voulons calculer start_date + n où n est compris entre 0 et la plage.

les règles définissent COMMENT nous allons remplir nos colonnes :

9        rules (the_date [for f from 0 to date_range[0] increment 1]  = start_date[0] + cv(f),
10               name[any] = name[0]);

donc avec 

the_date [for f from 0 to date_range[0] increment 1]

nous disons que nous allons générer le nombre de lignes que date_range contient + 1 (c'est-à-dire 6 dates au total). la valeur de f peut être référencé via le cv (valeur actuelle).

donc sur la ligne 1 pour david, nous aurions the_date [0] = start_date+0 et ensuite sur la ligne 2, nous aurions the_date [1] = start_date+1 . jusqu'à start_date+5 (c'est-à-dire la end_date )

p.s.pour se connecter par, vous devez faire quelque chose comme ceci :

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;

c'est-à-dire isoler la connexion par à une sous-requête, puis filtrer les lignes où individual_day> end_date.

mais je ne recommanderais PAS cette approche. ses performances seront moins bonnes par rapport à l'approche modèle (surtout si les plages deviennent grandes).