如何从 Oracle SQL 中的连续列中获取 运行 总数
How to get running total from consecutive columns in Oracle SQL
我无法显示 Oracle SQL 中现有日期数据集的连续假期。例如2017年12月20号到30号之间,有以下休息日(因为圣诞节和周末):
- 2017 年 12 月 23 日星期六
- 24.12.2017 星期日
- 25.12.2017 圣诞节
- 2017 年 12 月 30 日星期六
现在我希望我的结果数据集看起来像这样(需要 RUNTOT):
DAT ISOFF RUNTOT
20.12.2017 0 0
21.12.2017 0 0
22.12.2017 0 0
23.12.2017 1 1
24.12.2017 1 2
25.12.2017 1 3
26.12.2017 0 0
27.12.2017 0 0
28.12.2017 0 0
29.12.2017 0 0
30.12.2017 1 1
这意味着当 "ISOFF" 发生变化时,我想计算(或求和)"ISOFF" 为 1 的连续行。
我尝试使用分析函数来解决问题,我将 "ISOFF" 总结到当前行。
SELECT DAT,
ISOFF,
SUM (ISOFF)
OVER (ORDER BY DAT ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RUNTOT
FROM (TIME_DATASET)
WHERE DAT BETWEEN DATE '2017-12-20' AND DATE '2017-12-27'
ORDER BY 1
我现在得到的是以下数据集:
DAT ISOFF RUNTOT
20.12.2017 0 0
21.12.2017 0 0
22.12.2017 0 0
23.12.2017 1 1
24.12.2017 1 2
25.12.2017 1 3
26.12.2017 0 3
27.12.2017 0 3
28.12.2017 0 3
29.12.2017 0 3
30.12.2017 1 4
如果 ISOFF 变为 0,我如何重新设置 运行 总数?或者这是解决这个问题的错误方法?
感谢您的帮助!
这是一个缺口和孤岛问题。这是一种根据该行之前的 0 数量分配组的方法:
select t.*,
(case when is_off = 1
then row_number() over (partition by grp order by dat)
end) as runtot
from (select t.*,
sum(case when is_off = 0 then 1 else 0 end) over (order by dat) as grp
from TIME_DATASET t
) t;
您可以使用递归 recursive subquery factoring - 前提是,您的日期是连续的,没有间隔(或者您有一些其他 行号 顺序要遵循的步骤之一)。
WITH t1(dat, isoff, runtot) AS (
SELECT dat, isoff, 0 runtot
FROM tab
WHERE DAT = DATE'2017-12-20'
UNION ALL
SELECT t2.dat, t2.isoff,
case when t2.isoff = 0 then 0 else runtot + t2.isoff end as runtot
FROM tab t2, t1
WHERE t2.dat = t1.dat + 1
)
SELECT dat, isoff, runtot
FROM t1;
DAT ISOFF RUNTOT
------------------- ---------- ----------
20.12.2017 00:00:00 0 0
21.12.2017 00:00:00 0 0
22.12.2017 00:00:00 0 0
23.12.2017 00:00:00 1 1
24.12.2017 00:00:00 1 2
25.12.2017 00:00:00 1 3
26.12.2017 00:00:00 0 0
27.12.2017 00:00:00 0 0
28.12.2017 00:00:00 0 0
29.12.2017 00:00:00 0 0
30.12.2017 00:00:00 1 1
另一种不需要子查询或 CTE 但确实需要所有日期都存在且时间相同的变体是 - 仅针对假期日期(其中 isoff = 1
) - 看看如何自上次非节假日日期以来已经过去很多天了:
select dat,
isoff,
case
when isoff = 1 then
coalesce(dat - max(case when isoff = 0 then dat end)
over (order by dat range between unbounded preceding and 1 preceding), 1)
else 0
end as runtot
from time_dataset
order by dat;
DAT ISOFF RUNTOT
---------- ---------- ----------
2017-12-20 0 0
2017-12-21 0 0
2017-12-22 0 0
2017-12-23 1 1
2017-12-24 1 2
2017-12-25 1 3
2017-12-26 0 0
2017-12-27 0 0
2017-12-28 0 0
2017-12-29 0 0
2017-12-30 1 1
coalesce()
是为了防止范围内的第一个日期是节假日 - 因为没有之前的非节假日日期可以比较,所以减法会得到空值。
db<>fiddle数据集稍大。
我无法显示 Oracle SQL 中现有日期数据集的连续假期。例如2017年12月20号到30号之间,有以下休息日(因为圣诞节和周末):
- 2017 年 12 月 23 日星期六
- 24.12.2017 星期日
- 25.12.2017 圣诞节
- 2017 年 12 月 30 日星期六
现在我希望我的结果数据集看起来像这样(需要 RUNTOT):
DAT ISOFF RUNTOT
20.12.2017 0 0
21.12.2017 0 0
22.12.2017 0 0
23.12.2017 1 1
24.12.2017 1 2
25.12.2017 1 3
26.12.2017 0 0
27.12.2017 0 0
28.12.2017 0 0
29.12.2017 0 0
30.12.2017 1 1
这意味着当 "ISOFF" 发生变化时,我想计算(或求和)"ISOFF" 为 1 的连续行。
我尝试使用分析函数来解决问题,我将 "ISOFF" 总结到当前行。
SELECT DAT,
ISOFF,
SUM (ISOFF)
OVER (ORDER BY DAT ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RUNTOT
FROM (TIME_DATASET)
WHERE DAT BETWEEN DATE '2017-12-20' AND DATE '2017-12-27'
ORDER BY 1
我现在得到的是以下数据集:
DAT ISOFF RUNTOT
20.12.2017 0 0
21.12.2017 0 0
22.12.2017 0 0
23.12.2017 1 1
24.12.2017 1 2
25.12.2017 1 3
26.12.2017 0 3
27.12.2017 0 3
28.12.2017 0 3
29.12.2017 0 3
30.12.2017 1 4
如果 ISOFF 变为 0,我如何重新设置 运行 总数?或者这是解决这个问题的错误方法?
感谢您的帮助!
这是一个缺口和孤岛问题。这是一种根据该行之前的 0 数量分配组的方法:
select t.*,
(case when is_off = 1
then row_number() over (partition by grp order by dat)
end) as runtot
from (select t.*,
sum(case when is_off = 0 then 1 else 0 end) over (order by dat) as grp
from TIME_DATASET t
) t;
您可以使用递归 recursive subquery factoring - 前提是,您的日期是连续的,没有间隔(或者您有一些其他 行号 顺序要遵循的步骤之一)。
WITH t1(dat, isoff, runtot) AS (
SELECT dat, isoff, 0 runtot
FROM tab
WHERE DAT = DATE'2017-12-20'
UNION ALL
SELECT t2.dat, t2.isoff,
case when t2.isoff = 0 then 0 else runtot + t2.isoff end as runtot
FROM tab t2, t1
WHERE t2.dat = t1.dat + 1
)
SELECT dat, isoff, runtot
FROM t1;
DAT ISOFF RUNTOT
------------------- ---------- ----------
20.12.2017 00:00:00 0 0
21.12.2017 00:00:00 0 0
22.12.2017 00:00:00 0 0
23.12.2017 00:00:00 1 1
24.12.2017 00:00:00 1 2
25.12.2017 00:00:00 1 3
26.12.2017 00:00:00 0 0
27.12.2017 00:00:00 0 0
28.12.2017 00:00:00 0 0
29.12.2017 00:00:00 0 0
30.12.2017 00:00:00 1 1
另一种不需要子查询或 CTE 但确实需要所有日期都存在且时间相同的变体是 - 仅针对假期日期(其中 isoff = 1
) - 看看如何自上次非节假日日期以来已经过去很多天了:
select dat,
isoff,
case
when isoff = 1 then
coalesce(dat - max(case when isoff = 0 then dat end)
over (order by dat range between unbounded preceding and 1 preceding), 1)
else 0
end as runtot
from time_dataset
order by dat;
DAT ISOFF RUNTOT
---------- ---------- ----------
2017-12-20 0 0
2017-12-21 0 0
2017-12-22 0 0
2017-12-23 1 1
2017-12-24 1 2
2017-12-25 1 3
2017-12-26 0 0
2017-12-27 0 0
2017-12-28 0 0
2017-12-29 0 0
2017-12-30 1 1
coalesce()
是为了防止范围内的第一个日期是节假日 - 因为没有之前的非节假日日期可以比较,所以减法会得到空值。
db<>fiddle数据集稍大。