我正在尝试编写一个查询来获取会员的连续订阅日期,但无法拉取像下面这样的场景,其中会员的几个日期范围的 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;
从 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 |
这是一个可以获得您预期结果的查询
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 日 |