Oracle 中的分层查询

问题描述 投票:0回答:2

我正在尝试编写一个查询来获取会员的连续订阅日期,但无法拉取像下面这样的场景,其中会员的几个日期范围的 end_date 重叠,请帮助在 oracle 中提供逻辑/查询。

[表中数据][1]

身份证 开始日期 结束日期
12345 15 年 8 月 7 日 65 年 8 月 7 日
12345 2015 年 8 月 22 日 2016 年 1 月 1 日
12345 2016 年 3 月 24 日 66 年 3 月 23 日
12345 2016 年 7 月 6 日 2017 年 12 月 31 日
12345 2016 年 12 月 31 日 41 年 12 月 31 日
46628 2015 年 8 月 22 日 2015 年 12 月 22 日
46628 2016 年 1 月 1 日 2018 年 8 月 1 日
46628 2017 年 6 月 10 日 2018 年 12 月 31 日
46628 2018 年 12 月 1 日 72 年 12 月 4 日

[预期数据][2]

身份证 开始日期 结束日期
12345 15 年 8 月 7 日 66 年 3 月 23 日
46628 2015 年 8 月 22 日 2015 年 12 月 22 日
46628 2016 年 1 月 1 日 72 年 12 月 4 日

以下是我正在使用的逻辑,但它没有给出预期的结果,请帮忙。

    SELECT ID, 
        START_DATE, 
        END_DATE 
    FROM (
        SELECT ID,
            CONNECT_BY_ROOT START_DATE START_DATE,
            DAYS_DIFF, 
            END_DATE,
            PREV_END,
            CONNECT_BY_ISLEAF ISLEAF
        FROM ( 
            SELECT ID,
                DAYS_DIFF,PREV_END, START_DATE,END_DATE   
            FROM (   
                SELECT ID,
                    ROUND(START_DATE-PREV_END) DAYS_DIFF,
                    CASE 
                        WHEN START_DATE<=PREV_END THEN PREV_END 
                    END PREV_END, 
                    START_DATE,END_DATE
                FROM ( 
                    SELECT ID,
                        LAG(END_DATE) OVER (PARTITION BY ID ORDER BY START_DATE) PREV_END, 
                        START_DATE,
                        END_DATE  
                    FROM TEST_TABLE A 
                )                  
            )     
        ) 
        CONNECT BY ID= PRIOR ID AND   PREV_END=  PRIOR END_DATE    START WITH PREV_END IS NULL  
    ) WHERE ISLEAF=1; 

sql oracle connect hierarchical
2个回答
1
投票

从 Oracle 12 开始,您可以使用

MATCH_RECOGNIZE
进行逐行模式匹配:

SELECT *
FROM   table_name
       MATCH_RECOGNIZE(
         PARTITION BY id
         ORDER BY start_date, end_date
         MEASURES
           FIRST(start_date) AS start_date,
           MAX(end_date) AS end_date
         PATTERN (overlapping* final_range)
         DEFINE overlapping AS NEXT(start_date) <= MAX(end_date)
       )

在早期版本中,您可以使用:

SELECT id,
       MIN(dt) AS start_date,
       MAX(dt) AS end_date
FROM   (
  SELECT id,
         dt,
         SUM(group_change) OVER (PARTITION BY id ORDER BY dt)
          AS grp
  FROM   (
    SELECT id,
           dt,
           CASE type * SUM(type) OVER (PARTITION BY id ORDER BY dt, type DESC, ROWNUM)
           WHEN 1
           THEN 1
           ELSE 0
           END AS group_change
    FROM   table_name
           UNPIVOT (dt FOR type IN (start_date AS +1, end_date AS -1))
  )
)
GROUP BY id, grp

对于样本数据:

CREATE TABLE table_name (id, start_date, end_date) AS
  SELECT   12345, DATE '2015-08-07', DATE '2065-08-07' FROM DUAL UNION ALL
  SELECT   12345, DATE '2015-08-22', DATE '2016-01-01' FROM DUAL UNION ALL
  SELECT   12345, DATE '2016-03-24', DATE '2066-03-23' FROM DUAL UNION ALL
  SELECT   12345, DATE '2016-07-06', DATE '2017-12-31' FROM DUAL UNION ALL
  SELECT   12345, DATE '2016-12-31', DATE '2041-12-31' FROM DUAL UNION ALL
  SELECT 4662828, DATE '2015-08-22', DATE '2015-12-22' FROM DUAL UNION ALL
  SELECT 4662828, DATE '2016-01-01', DATE '2018-08-01' FROM DUAL UNION ALL
  SELECT 4662828, DATE '2017-06-10', DATE '2018-12-31' FROM DUAL UNION ALL
  SELECT 4662828, DATE '2018-12-01', DATE '2072-12-04' FROM DUAL;

两个输出:

身份证 START_DATE END_DATE
12345 2015-08-07 00:00:00 2066-03-23 00:00:00
4662828 2015-08-22 00:00:00 2015-12-22 00:00:00
4662828 2016-01-01 00:00:00 2072-12-04 00:00:00

小提琴


0
投票

这是一个可以获得您预期结果的查询

WITH tmp AS
(
    SELECT 12345 AS id,     TO_DATE('07-Aug-15', 'DD/MON/YY') AS Start_date,    TO_DATE('07-Aug-65', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    SELECT 12345 AS id,     TO_DATE('22-Aug-15', 'DD/MON/YY') AS Start_date,    TO_DATE('01-Jan-16', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    SELECT 12345 AS id,     TO_DATE('24-Mar-16', 'DD/MON/YY') AS Start_date,    TO_DATE('23-Mar-66', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    SELECT 12345 AS id,     TO_DATE('06-Jul-16', 'DD/MON/YY') AS Start_date,    TO_DATE('31-Dec-17', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    SELECT 12345 AS id,     TO_DATE('31-Dec-16', 'DD/MON/YY') AS Start_date,    TO_DATE('31-Dec-41', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    
    SELECT 4662828 AS id,   TO_DATE('22-Aug-15', 'DD/MON/YY') AS Start_date,    TO_DATE('22-Dec-15', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    SELECT 4662828 AS id,   TO_DATE('01-Jan-16', 'DD/MON/YY') AS Start_date,    TO_DATE('01-Aug-18', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    SELECT 4662828 AS id,   TO_DATE('10-Jun-17', 'DD/MON/YY') AS Start_date,    TO_DATE('31-Dec-18', 'DD/MON/YY') AS End_date FROM dual UNION ALL
    SELECT 4662828 AS id,   TO_DATE('01-Dec-18', 'DD/MON/YY') AS Start_date,    TO_DATE('04-Dec-72', 'DD/MON/YY') AS End_date FROM dual 
)
, tmp1 AS
(
    SELECT 
        CONNECT_BY_ROOT t.start_date AS start_date,
        t.id,
        t.start_date AS start_date1,
        t.end_date,
        level AS lvl
    FROM tmp t
    CONNECT BY PRIOR id = id AND PRIOR start_date <> start_date AND PRIOR end_date <> end_date 
        AND PRIOR end_date BETWEEN start_date AND end_date
)
, tmp2 AS 
(
    SELECT *
    FROM tmp1
    WHERE (start_date, id, lvl) IN
        (
            SELECT start_date, id, MAX(lvl)
            FROM tmp1
            GROUP BY start_date, id
        )
)
SELECT id,
    MIN(start_date) AS start_date,
    end_date
FROM tmp2
GROUP BY id, end_date
ORDER BY id, start_date;
身份证 START_DATE END_DATE
12345 15 年 8 月 7 日 66 年 3 月 23 日
4662828 2015 年 8 月 22 日 15 年 12 月 22 日
4662828 2016 年 1 月 1 日 72 年 12 月 4 日

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.