基于this answer(其中包含查询的详细说明),您可以更改最后部分以生成重叠行组,然后使用LISTAGG聚合以获得分隔的sno值:
SELECT id,
MIN( dt ) AS start_date,
MAX( dt ) AS end_date,
LISTAGG( CASE value WHEN 1 THEN sno END, ',' )
WITHIN GROUP ( ORDER BY dt ) AS snos
FROM (
SELECT sno,
id,
dt,
value,
SUM( start_end ) OVER ( ORDER BY dt ASC, value DESC )
AS grp
FROM (
SELECT sno,
id,
dt,
value,
CASE SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) * value
WHEN 1 THEN 1
WHEN 0 THEN 0
ELSE NULL
END AS start_end
FROM table_name
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
)
)
GROUP BY id, grp;
其中,对于样本数据:
CREATE TABLE table_name ( sno, id, start_date, end_date ) AS
SELECT 1, 1, DATE '2020-10-11', DATE '2020-10-11' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2020-11-04', DATE '2020-12-11' FROM DUAL UNION ALL
SELECT 3, 1, DATE '2020-11-05', DATE '2020-11-10' FROM DUAL UNION ALL
SELECT 4, 1, DATE '2020-11-06', DATE '2020-11-10' FROM DUAL UNION ALL
SELECT 5, 1, DATE '2020-11-20', DATE '2020-12-20' FROM DUAL UNION ALL
SELECT 6, 1, DATE '2021-01-01', DATE '2021-01-20' FROM DUAL UNION ALL
SELECT 7, 1, DATE '2021-01-01', DATE '2021-03-25' FROM DUAL;
输出:
身份证 | START_DATE | END_DATE | SNOS
-: | :----------------- | :----------------- | :------
1 | 2020-10-11 00:00:00 | 2020-10-11 00:00:00 | 1
1 | 2020-11-04 00:00:00 | 2020-12-20 00:00:00 | 2,3,4,5
1 | 2021-01-01 00:00:00 | 2021-03-25 00:00:00 | 6,7
db小提琴here
从 Oracle 12c 开始,您可以使用 MATCH_RECOGNIZE 进行更简单的查询:
SELECT id,
MIN( start_date ) AS start_date,
MAX( end_date ) AS end_date,
LISTAGG( sno, ',' ) WITHIN GROUP ( ORDER BY start_date ASC, END_DATE ASC )
AS snos
FROM table_name
MATCH_RECOGNIZE (
PARTITION BY id
ORDER BY start_date ASC, end_date ASC
MEASURES MATCH_NUMBER() AS grp
ALL ROWS PER MATCH
PATTERN ( OVERLAPPING_DATES* LAST_DATE )
DEFINE OVERLAPPING_DATES AS NEXT( start_date ) <= MAX( end_date )
)
GROUP BY id, grp
db小提琴here