测试数据:
CREATE TABLE your_table ( usr, start_date, end_date ) AS (
SELECT 'A', DATE '2017-06-01', DATE '2017-06-03' FROM DUAL UNION ALL
SELECT 'B', DATE '2017-06-02', DATE '2017-06-04' FROM DUAL UNION ALL -- Overlaps previous
SELECT 'C', DATE '2017-06-06', DATE '2017-06-06' FROM DUAL UNION ALL
SELECT 'D', DATE '2017-06-07', DATE '2017-06-07' FROM DUAL UNION ALL -- Adjacent to previous
SELECT 'E', DATE '2017-06-11', DATE '2017-06-20' FROM DUAL UNION ALL
SELECT 'F', DATE '2017-06-14', DATE '2017-06-15' FROM DUAL UNION ALL -- Within previous
SELECT 'G', DATE '2017-06-22', DATE '2017-06-25' FROM DUAL UNION ALL
SELECT 'H', DATE '2017-06-24', DATE '2017-06-28' FROM DUAL UNION ALL -- Overlaps previous and next
SELECT 'I', DATE '2017-06-27', DATE '2017-06-30' FROM DUAL UNION ALL
SELECT 'J', DATE '2017-06-27', DATE '2017-06-28' FROM DUAL; -- Within H and I
查询:
SELECT SUM( days ) AS total_days
FROM (
SELECT dt - LAG( dt ) OVER ( ORDER BY dt ) + 1 AS days,
start_end
FROM (
SELECT dt,
CASE SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) * value
WHEN 1 THEN 'start'
WHEN 0 THEN 'end'
END AS start_end
FROM your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
)
WHERE start_end IS NOT NULL
)
WHERE start_end = 'end';
输出:
TOTAL_DAYS
----------
25
说明:
SELECT dt, value
FROM your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
这将UNPIVOT 表格,以便开始日期和结束日期在同一列中 (dt),并为开始日期指定相应的值 +1,为结束日期指定 -1。
SELECT dt,
SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) AS total,
value
FROM your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
将给出开始和结束日期以及这些生成值的累积总和。范围的开头总是有value=1 和total=1,范围的结尾总是有total=0。如果日期在某个范围的中间,那么它将具有total>1 或value=-1 和total=1。使用它,如果您将value 和total 相乘,则范围的开始是value*total=1 的时间,范围的结束时间是value*total=0 的时间,并且任何其他值都表示日期在范围的中间。
这是什么意思:
SELECT dt,
CASE SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) * value
WHEN 1 THEN 'start'
WHEN 0 THEN 'end'
END AS start_end
FROM your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
然后,您可以过滤掉 start_end 为 NULL 的日期,这将为您留下一个表格,其中包含交替的 start 和 end 行,您可以使用 LAG 来计算天数差异:
SELECT dt - LAG( dt ) OVER ( ORDER BY dt ) + 1 AS days,
start_end
FROM (
SELECT dt,
CASE SUM( value ) OVER ( ORDER BY dt ASC, value DESC, ROWNUM ) * value
WHEN 1 THEN 'start'
WHEN 0 THEN 'end'
END AS start_end
FROM your_table
UNPIVOT ( dt FOR value IN ( start_date AS 1, end_date AS -1 ) )
)
WHERE start_end IS NOT NULL
那么你需要做的就是SUMend - start的所有差异;这给出了上面的查询。