我有以下(简化)表(Oracle DB):
id | 创建于 | 已解决_at |
---|---|---|
1 | 2023-10-15 | 2023-10-29 |
2 | 2023-10-20 | |
3 | 2023-11-01 | 2023-12-02 |
4 | 2023-11-15 | 2023-11-20 |
5 | 2023-12-03 | 2024-01-10 |
6 | 2024-01-04 | 2024-01-10 |
我想计算每月门票开放的天数。重要的是,开放门票将包含在开放的每个月的计算中。
从上面的例子来看,它将是:
2023-10:从工单 1 算起 4 天(解决时间 - 创建时间),从工单 2 算起 11 天(因为记录是开放的/整个月都没有解决)。所以平均开放天数为 (4 + 12) / 2 = 8
2023-11:从票证 2 起 30 天(因为它仍然开放)+ 从票证 3 起 30 天(也开放整个月)+ 从票证 4 起 5 天。平均开放天数为 65 / 3 = 21,6 天。
我对 SQL 并不陌生,但我从未遇到过类似的事情。理论上,可以从临时表/硬编码中选择我想要的月份并连接记录,但是没有更好的解决方案吗?
生成日历,然后外连接到日历并取该月内天数的平均值:
WITH calendar (month) AS (
SELECT ADD_MONTHS(TRUNC(min_dt, 'MM'), LEVEL - 1)
FROM (
SELECT MIN(created_at) AS min_dt,
MAX(COALESCE(solved_at, SYSDATE)) AS max_dt
FROM table_name
)
CONNECT BY ADD_MONTHS(TRUNC(min_dt, 'MM'), LEVEL - 1) <= max_dt
)
SELECT TO_CHAR(c.month, 'YYYY-MM') AS month,
AVG(
LEAST(COALESCE(t.solved_at, TRUNC(SYSDATE)), ADD_MONTHS(c.month, 1))
- GREATEST(t.created_at, c.month)
) AS avg_days
FROM calendar c
LEFT OUTER JOIN table_name t
ON (c.month < COALESCE(t.solved_at, SYSDATE)
AND t.created_at < ADD_MONTHS(c.month, 1))
GROUP BY
c.month
对于样本数据:
create table table_name (id, created_at, solved_at) AS
SELECT 1, DATE '2023-10-15', DATE '2023-10-29' FROM DUAL UNION ALL
SELECT 2, DATE '2023-10-20', NULL FROM DUAL UNION ALL
SELECT 3, DATE '2023-11-01', DATE '2023-12-02' FROM DUAL UNION ALL
SELECT 4, DATE '2023-11-15', DATE '2023-11-20' FROM DUAL UNION ALL
SELECT 5, DATE '2023-12-03', DATE '2024-01-10' FROM DUAL UNION ALL
SELECT 6, DATE '2024-01-04', DATE '2024-01-10' FROM DUAL;
输出:
月 | AVG_DAYS |
---|---|
2023-10 | 13 |
2023-11 | 21.66666666666666666666666666666666666667 |
2023-12 | 20.33333333333333333333333333333333333333 |
2024-01 | 15.33333333333333333333333333333333333333 |
2024-02 | 29 |
2024-03 | 31 |
2024-04 | 14 |