Oracle select 中带有 window 函数的 运行 总数(累计列)有什么问题?
What is wrong with this running total (cumulative column) in Oracle select with a window function?
我有一个列中包含 0
或 1
的查询。出于演示目的,我将 1
替换为 77
以便更容易看到:
select dates.d the_date
, case TO_CHAR(dates.d, 'd') when '7' then 0 when '1' then 0
else 77
end as is_workday
from (SELECT (to_date('01.01.2019','dd.mm.yyyy') + (LEVEL -1))
AS d FROM DUAL connect by level <=(
to_date('31.12.2020','dd.mm.yyyy')-(to_date('01.01.2019','dd.mm.yyyy')))
) as dates
如果此日期是否为工作日,则结果只是一个连续的日期列和一个标记。 (在现实生活中我也计算假期,但这不是问题):
| THE_DATE | IS_WORKDAY |
| 2019-01-01 00:00:00 | 77 |
| 2019-01-02 00:00:00 | 77 |
| 2019-01-03 00:00:00 | 77 |
| 2019-01-04 00:00:00 | 77 |
| 2019-01-05 00:00:00 | 0 |
| 2019-01-06 00:00:00 | 0 |
| 2019-01-07 00:00:00 | 77 |
| 2019-01-08 00:00:00 | 77 |
| 2019-01-09 00:00:00 | 77 |
| 2019-01-10 00:00:00 | 77 |
....
我想在 is_workday
上加一个 运行 总数,意思是累计值。我确信 Oracles window 函数就是为此而设计的。
SELECT x.the_date
, x.is_workday
, sum(x.is_workday) over (
partition by x.the_date -- define the window
order by x.the_date asc -- order inside window
rows between unbounded preceding -- sum to top
and current row -- sum ending here
) as workdays_cumul
FROM (
select dates.d the_date
, case TO_CHAR(dates.d, 'd') when '7' then 0 when '1' then 0
else 77
end as is_workday
from (SELECT (to_date('01.01.2019','dd.mm.yyyy') + (LEVEL -1))
AS d FROM DUAL connect by level <=(
to_date('31.12.2020','dd.mm.yyyy')-(to_date('01.01.2019','dd.mm.yyyy')))
) as dates
) x
order by x.the_date
;
但我必须在这里遗漏一些东西,因为我 没有 得到 运行 总数,而只是价值本身。
| THE_DATE | IS_WORKDAY | WORKDAYS_CUMUL |
| 2019-01-01 00:00:00 | 77 | 77 |
| 2019-01-02 00:00:00 | 77 | 77 |
| 2019-01-03 00:00:00 | 77 | 77 |
| 2019-01-04 00:00:00 | 77 | 77 |
| 2019-01-05 00:00:00 | 0 | 0 |
| 2019-01-06 00:00:00 | 0 | 0 |
| 2019-01-07 00:00:00 | 77 | 77 |
| 2019-01-08 00:00:00 | 77 | 77 |
....
显然应该是:
| THE_DATE | IS_WORKDAY | WORKDAYS_CUMUL |
| 2019-01-01 00:00:00 | 77 | 77 |
| 2019-01-02 00:00:00 | 77 | 154 |
| 2019-01-03 00:00:00 | 77 | 231 |
...
我以为它会像这样:
sum(x.is_workday)
-- 对 77
值 求和
partition by x.the_date
-- 使 windows/sections/parts 每行一行(在我的例子中)
order by x.the_date asc
-- 按日期排序这些行
rows between unbounded preceding
-- 第一行之间的总和...
and current row
-- ...和当前行。
我在这里错过了什么?
删除查询的 PARTITION BY
子句,因为您希望 window 的范围是整个查询而不是每个单独的日期:
SELECT the_date
, is_workday
, sum(is_workday) over (
ORDER BY the_date asc -- order inside window
ROWS BETWEEN unbounded preceding -- sum to top
AND current row -- sum ending here
) as workdays_cumul
FROM (
select d the_date
, CASE
WHEN d - TRUNC( d, 'IW' ) IN ( 5, 6 )
THEN 0
ELSE 1
END AS is_workday
FROM (
SELECT DATE '2019-01-01' + LEVEL -1 AS d
FROM DUAL
connect by level <= DATE '2020-12-31' - DATE '2019-01-01'
)
)
order by the_date;
你也可以使用日期文字,需要删除 table 别名中的 AS
(并且实际上不需要任何 table 别名)并且可以使用之间的区别日期和其 ISO 周的开始 (d - TRUNC( d, 'IW' )
) 作为查找星期六和星期日的方法,独立于 NLS_TERRITORY
会话参数。
您也可以删除 ROWS BETWEEN
子句,因为 ROWS BETWEEN unbounded preceding AND current row
是默认行为。但是,如果这是您需要的行为,那么您也可以保留它以证明 window 是您的预期结果。
输出:
THE_DATE | IS_WORKDAY | WORKDAYS_CUMUL
:-------- | ---------: | -------------:
01-JAN-19 | 1 | 1
02-JAN-19 | 1 | 2
03-JAN-19 | 1 | 3
04-JAN-19 | 1 | 4
05-JAN-19 | 0 | 4
06-JAN-19 | 0 | 4
07-JAN-19 | 1 | 5
08-JAN-19 | 1 | 6
09-JAN-19 | 1 | 7
...
24-DEC-20 | 1 | 518
25-DEC-20 | 1 | 519
26-DEC-20 | 0 | 519
27-DEC-20 | 0 | 519
28-DEC-20 | 1 | 520
29-DEC-20 | 1 | 521
30-DEC-20 | 1 | 522
db<>fiddle here
我有一个列中包含 0
或 1
的查询。出于演示目的,我将 1
替换为 77
以便更容易看到:
select dates.d the_date
, case TO_CHAR(dates.d, 'd') when '7' then 0 when '1' then 0
else 77
end as is_workday
from (SELECT (to_date('01.01.2019','dd.mm.yyyy') + (LEVEL -1))
AS d FROM DUAL connect by level <=(
to_date('31.12.2020','dd.mm.yyyy')-(to_date('01.01.2019','dd.mm.yyyy')))
) as dates
如果此日期是否为工作日,则结果只是一个连续的日期列和一个标记。 (在现实生活中我也计算假期,但这不是问题):
| THE_DATE | IS_WORKDAY |
| 2019-01-01 00:00:00 | 77 |
| 2019-01-02 00:00:00 | 77 |
| 2019-01-03 00:00:00 | 77 |
| 2019-01-04 00:00:00 | 77 |
| 2019-01-05 00:00:00 | 0 |
| 2019-01-06 00:00:00 | 0 |
| 2019-01-07 00:00:00 | 77 |
| 2019-01-08 00:00:00 | 77 |
| 2019-01-09 00:00:00 | 77 |
| 2019-01-10 00:00:00 | 77 |
....
我想在 is_workday
上加一个 运行 总数,意思是累计值。我确信 Oracles window 函数就是为此而设计的。
SELECT x.the_date
, x.is_workday
, sum(x.is_workday) over (
partition by x.the_date -- define the window
order by x.the_date asc -- order inside window
rows between unbounded preceding -- sum to top
and current row -- sum ending here
) as workdays_cumul
FROM (
select dates.d the_date
, case TO_CHAR(dates.d, 'd') when '7' then 0 when '1' then 0
else 77
end as is_workday
from (SELECT (to_date('01.01.2019','dd.mm.yyyy') + (LEVEL -1))
AS d FROM DUAL connect by level <=(
to_date('31.12.2020','dd.mm.yyyy')-(to_date('01.01.2019','dd.mm.yyyy')))
) as dates
) x
order by x.the_date
;
但我必须在这里遗漏一些东西,因为我 没有 得到 运行 总数,而只是价值本身。
| THE_DATE | IS_WORKDAY | WORKDAYS_CUMUL |
| 2019-01-01 00:00:00 | 77 | 77 |
| 2019-01-02 00:00:00 | 77 | 77 |
| 2019-01-03 00:00:00 | 77 | 77 |
| 2019-01-04 00:00:00 | 77 | 77 |
| 2019-01-05 00:00:00 | 0 | 0 |
| 2019-01-06 00:00:00 | 0 | 0 |
| 2019-01-07 00:00:00 | 77 | 77 |
| 2019-01-08 00:00:00 | 77 | 77 |
....
显然应该是:
| THE_DATE | IS_WORKDAY | WORKDAYS_CUMUL |
| 2019-01-01 00:00:00 | 77 | 77 |
| 2019-01-02 00:00:00 | 77 | 154 |
| 2019-01-03 00:00:00 | 77 | 231 |
...
我以为它会像这样:
sum(x.is_workday)
-- 对77
值 求和
partition by x.the_date
-- 使 windows/sections/parts 每行一行(在我的例子中)order by x.the_date asc
-- 按日期排序这些行rows between unbounded preceding
-- 第一行之间的总和...and current row
-- ...和当前行。
我在这里错过了什么?
删除查询的 PARTITION BY
子句,因为您希望 window 的范围是整个查询而不是每个单独的日期:
SELECT the_date
, is_workday
, sum(is_workday) over (
ORDER BY the_date asc -- order inside window
ROWS BETWEEN unbounded preceding -- sum to top
AND current row -- sum ending here
) as workdays_cumul
FROM (
select d the_date
, CASE
WHEN d - TRUNC( d, 'IW' ) IN ( 5, 6 )
THEN 0
ELSE 1
END AS is_workday
FROM (
SELECT DATE '2019-01-01' + LEVEL -1 AS d
FROM DUAL
connect by level <= DATE '2020-12-31' - DATE '2019-01-01'
)
)
order by the_date;
你也可以使用日期文字,需要删除 table 别名中的 AS
(并且实际上不需要任何 table 别名)并且可以使用之间的区别日期和其 ISO 周的开始 (d - TRUNC( d, 'IW' )
) 作为查找星期六和星期日的方法,独立于 NLS_TERRITORY
会话参数。
您也可以删除 ROWS BETWEEN
子句,因为 ROWS BETWEEN unbounded preceding AND current row
是默认行为。但是,如果这是您需要的行为,那么您也可以保留它以证明 window 是您的预期结果。
输出:
THE_DATE | IS_WORKDAY | WORKDAYS_CUMUL :-------- | ---------: | -------------: 01-JAN-19 | 1 | 1 02-JAN-19 | 1 | 2 03-JAN-19 | 1 | 3 04-JAN-19 | 1 | 4 05-JAN-19 | 0 | 4 06-JAN-19 | 0 | 4 07-JAN-19 | 1 | 5 08-JAN-19 | 1 | 6 09-JAN-19 | 1 | 7 ... 24-DEC-20 | 1 | 518 25-DEC-20 | 1 | 519 26-DEC-20 | 0 | 519 27-DEC-20 | 0 | 519 28-DEC-20 | 1 | 520 29-DEC-20 | 1 | 521 30-DEC-20 | 1 | 522
db<>fiddle here