截断多个日期

时间:2018-05-21 21:04:49

标签: sql oracle

这是一张图片,描述了我使用的表格(TBL_CHILDREN)以及我想要实现的所需输出。

enter image description here

所需的表希望在相同的PARENT_ID下为每个新的活动CHILD_ID组合设置单独的行。因此,例如,从2017-01-01到2017-02-28只有CHILD_ID 1处于活动状态,因此所需表格的行跨越2017-01-01到2017-02-28。但是在2017-03-01 CHILD_ID 2也生效了,所以我需要一个新行来反映CHILD_ID 1和2同时处于活动状态的时间段。依此类推,直到我有一行描述CHILD_ID组合的每个时期。

以下是TBL_CHILDREN的一些代码:

WITH TBL_CHILDREN AS (SELECT 57 PARENT_ID, 1 CHILD_ID, TO_DATE('2017-01-01','YYYY-MM-DD') START_DATE, TO_DATE('9999-12-31','YYYY-MM-DD') END_DATE FROM dual UNION ALL
                      SELECT 57 PARENT_ID, 2 CHILD_ID, TO_DATE('2017-03-01','YYYY-MM-DD') START_DATE, TO_DATE('2017-05-31','YYYY-MM-DD') END_DATE FROM dual UNION ALL
                      SELECT 57 PARENT_ID, 3 CHILD_ID, TO_DATE('2017-04-01','YYYY-MM-DD') START_DATE, TO_DATE('2017-10-31','YYYY-MM-DD') END_DATE FROM dual)
SELECT *
FROM TBL_CHILDREN

2 个答案:

答案 0 :(得分:2)

UNPIVOTLAGLEAD分析函数配合使用,可以在单个表扫描中执行此操作:

SQL Fiddle

Oracle 11g R2架构设置

create table TBL_CHILDREN ( parent_id, child_id, start_date, end_date )AS
SELECT 57, 1, DATE '2017-01-01', DATE '9999-12-31' FROM dual UNION ALL
SELECT 57, 2, DATE '2017-03-01', DATE '2017-05-31' FROM dual UNION ALL
SELECT 57, 3, DATE '2017-04-01', DATE '2017-10-31' FROM dual;

查询1

SELECT *
FROM   (
  SELECT PARENT_ID,
         DT AS start_date,
         LEAD( DT ) OVER ( PARTITION BY parent_id ORDER BY DT ) AS end_date
  FROM   TBL_CHILDREN
  UNPIVOT( dt FOR start_end IN ( start_date, end_date ) )
)
WHERE  end_date IS NOT NULL

<强> Results

| PARENT_ID |           START_DATE |             END_DATE |
|-----------|----------------------|----------------------|
|        57 | 2017-01-01T00:00:00Z | 2017-03-01T00:00:00Z |
|        57 | 2017-03-01T00:00:00Z | 2017-04-01T00:00:00Z |
|        57 | 2017-04-01T00:00:00Z | 2017-05-31T00:00:00Z |
|        57 | 2017-05-31T00:00:00Z | 2017-10-31T00:00:00Z |
|        57 | 2017-10-31T00:00:00Z | 9999-12-31T00:00:00Z |

查询2 ,这将获得每个时间段的父ID和子ID:

SELECT *
FROM   (
  SELECT parent_id,
         ( SELECT LISTAGG( child_id, ',' ) WITHIN GROUP ( ORDER BY child_id )
           FROM   TBL_CHILDREN c
           WHERE  u.dt >= c.START_DATE
           AND    u.dt <  c.END_DATE ) AS child_ids,
         DT AS start_date,
         LEAD( DT ) OVER ( PARTITION BY parent_id ORDER BY DT ) AS end_date
  FROM   TBL_CHILDREN
  UNPIVOT( dt FOR start_end IN ( start_date, end_date ) ) u
)
WHERE  end_date IS NOT NULL

<强> Results

| PARENT_ID | CHILD_IDS |           START_DATE |             END_DATE |
|-----------|-----------|----------------------|----------------------|
|        57 |         1 | 2017-01-01T00:00:00Z | 2017-03-01T00:00:00Z |
|        57 |       1,2 | 2017-03-01T00:00:00Z | 2017-04-01T00:00:00Z |
|        57 |     1,2,3 | 2017-04-01T00:00:00Z | 2017-05-31T00:00:00Z |
|        57 |       1,3 | 2017-05-31T00:00:00Z | 2017-10-31T00:00:00Z |
|        57 |         1 | 2017-10-31T00:00:00Z | 9999-12-31T00:00:00Z |

答案 1 :(得分:1)

请查看this demo

WITH qqq AS (
  SELECT * FROM TBL_CHILDREN
  START WITH child_id = 1
  CONNECT BY PRIOR parent_id = parent_id AND PRIOR child_id + 1 = child_id
)
SELECT * FROM (
  SELECT PARENT_ID, 
         d as start_date,
         lead(d) over (partition by PARENT_ID order by d ) - 1 as end_date
  FROM (
    SELECT PARENT_ID, start_date as d FROM qqq
    UNION
    SELECT PARENT_ID, end_date FROM qqq
  )
)
WHERE end_date is not null
ORDER by PARENT_ID, start_date
;

| PARENT_ID |           START_DATE |             END_DATE |
|-----------|----------------------|----------------------|
|        57 | 2017-01-01T00:00:00Z | 2017-02-28T00:00:00Z |
|        57 | 2017-03-01T00:00:00Z | 2017-03-31T00:00:00Z |
|        57 | 2017-04-01T00:00:00Z | 2017-05-30T00:00:00Z |
|        57 | 2017-05-31T00:00:00Z | 2017-10-30T00:00:00Z |
|        57 | 2017-10-31T00:00:00Z | 9999-12-30T00:00:00Z |