Oracle SQL 收入 YTD 计算
Oracle SQL revenue YTD computation
我想编写一个 oracle SQL 查询来计算给定维度的所有可能组合的每月 YTD 收入(累计总和)。还有一些月份没有交易,因此没有收入,在这种情况下,必须为该维度组合显示上个月 YTD 收入。给定 table:
| Month | site | channel | type | revenue |
| ----- | ---- | ------- | ---- | ------- |
| 2017-02 | abc | 1 | A | 50 |
| 2017-04 | abc | 2 | B | 100 |
| 2018-12 | xyz | 1 | A | 150 |
示例所需输出:
| Month | site | channel | type | ytd revenue |
| ----- | ---- | ------- | ---- | ------- |
| 2017-01 | abc | 1 | A | 0 |
| 2017-02 | abc | 1 | A | 50 |
| 2017-03 | abc | 1 | A | 50 |
| 2017-04 | abc | 1 | A | 50 |
| ------ | --- | -- | -- | --- |
| 2018-12 | abc | 1 | A | 1000 |
| ----- | -- | -- | -- | --- |
| 2017-04 | abc | 2 | A | 100 |
| ---- | --- | - | - | -- |
| 2018-12 | abc | 2 | A | 10 |
| --- | -- | - | - | -- |
| 2018-12 | xyz | 1 | A | 150 |
会计年度从第 1 个月开始,到第 12 个月结束。因此,对于所有维度组合,累计总和或 YTD 收入必须是每年第 1 个月到第 12 个月,如上面的示例输出所示。
如果我没理解错的话,您可以使用cross join
获取所有行,然后使用left join
和累加总和来获取最新值:
select m.month, sc.site, sc.channel, sc.type,
sum(revenue) over (partition by sc.site, sc.channel, sc.type, trunc(m.month, 'YYYY') order by m.month) as ytd_revenue
from (select distinct month from t) m cross join
(select distinct site, channel, type from t) sct left join
t
on t.month = m.month and t.site = sct.site and
t.channel = sc.channel and t.type = sct.type;
这假设所有月份都在数据中可用。如果没有,您需要生成 months 。 . .使用显式列表或使用某种生成器,例如:
with months(month) as (
select date '2019-01-01' as month
from dual
union all
select month + interval '1' month
from months
where month < date '2021-1-01'
)
使用 PARTITION OUTER JOIN
:
SELECT ADD_MONTHS( t.year, c.month - 1 ) AS month,
t.site,
t.channel,
t.type,
SUM( COALESCE( t.revenue, 0 ) ) OVER (
PARTITION BY t.site, t.channel, t.type, t.year
ORDER BY c.month
) AS ytd_revenue
FROM (
SELECT LEVEL AS month
FROM DUAL
CONNECT BY LEVEL <= 12
) c
LEFT OUTER JOIN (
SELECT t.*,
TRUNC( month, 'YY' ) AS year
FROM table_name t
) t
PARTITION BY ( site, channel, type, year )
ON ( c.month = EXTRACT( MONTH FROM t.month ) );
其中,对于示例数据:
CREATE TABLE table_name ( Month, site, channel, type, revenue ) AS
SELECT DATE '2017-02-01', 'abc', 1, 'A', 50 FROM DUAL UNION ALL
SELECT DATE '2017-04-01', 'abc', 2, 'B', 100 FROM DUAL UNION ALL
SELECT DATE '2018-12-01', 'xyz', 1, 'A', 150 FROM DUAL;
输出:
MONTH | SITE | CHANNEL | TYPE | YTD_REVENUE
:------------------ | :--- | ------: | :--- | ----------:
2017-01-01 00:00:00 | abc | 1 | A | 0
2017-02-01 00:00:00 | abc | 1 | A | 50
2017-03-01 00:00:00 | abc | 1 | A | 50
2017-04-01 00:00:00 | abc | 1 | A | 50
2017-05-01 00:00:00 | abc | 1 | A | 50
2017-06-01 00:00:00 | abc | 1 | A | 50
2017-07-01 00:00:00 | abc | 1 | A | 50
2017-08-01 00:00:00 | abc | 1 | A | 50
2017-09-01 00:00:00 | abc | 1 | A | 50
2017-10-01 00:00:00 | abc | 1 | A | 50
2017-11-01 00:00:00 | abc | 1 | A | 50
2017-12-01 00:00:00 | abc | 1 | A | 50
2017-01-01 00:00:00 | abc | 2 | B | 0
2017-02-01 00:00:00 | abc | 2 | B | 0
2017-03-01 00:00:00 | abc | 2 | B | 0
2017-04-01 00:00:00 | abc | 2 | B | 100
2017-05-01 00:00:00 | abc | 2 | B | 100
2017-06-01 00:00:00 | abc | 2 | B | 100
2017-07-01 00:00:00 | abc | 2 | B | 100
2017-08-01 00:00:00 | abc | 2 | B | 100
2017-09-01 00:00:00 | abc | 2 | B | 100
2017-10-01 00:00:00 | abc | 2 | B | 100
2017-11-01 00:00:00 | abc | 2 | B | 100
2017-12-01 00:00:00 | abc | 2 | B | 100
2018-01-01 00:00:00 | xyz | 1 | A | 0
2018-02-01 00:00:00 | xyz | 1 | A | 0
2018-03-01 00:00:00 | xyz | 1 | A | 0
2018-04-01 00:00:00 | xyz | 1 | A | 0
2018-05-01 00:00:00 | xyz | 1 | A | 0
2018-06-01 00:00:00 | xyz | 1 | A | 0
2018-07-01 00:00:00 | xyz | 1 | A | 0
2018-08-01 00:00:00 | xyz | 1 | A | 0
2018-09-01 00:00:00 | xyz | 1 | A | 0
2018-10-01 00:00:00 | xyz | 1 | A | 0
2018-11-01 00:00:00 | xyz | 1 | A | 0
2018-12-01 00:00:00 | xyz | 1 | A | 150
或者,如果您想要完整的日期范围而不是每年:
WITH calendar ( month ) AS (
SELECT ADD_MONTHS( start_month, LEVEL - 1 )
FROM (
SELECT MIN( ADD_MONTHS( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ), 3 ) ) AS start_month,
ADD_MONTHS( MAX( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) ), 14 ) AS end_month
FROM table_name
)
CONNECT BY
ADD_MONTHS( start_month, LEVEL - 1 ) <= end_month
)
SELECT TO_CHAR( c.month, 'YYYY-MM' ) AS month,
t.site,
t.channel,
t.type,
SUM( COALESCE( t.revenue, 0 ) ) OVER (
PARTITION BY t.site, t.channel, t.type, TRUNC( c.month, 'YY' )
ORDER BY c.month
) AS ytd_revenue
FROM calendar c
LEFT OUTER JOIN (
SELECT t.*,
TRUNC( month, 'YY' ) AS year
FROM table_name t
) t
PARTITION BY ( site, channel, type )
ON ( c.month = t.month )
ORDER BY
site, channel, type, month;
输出:
MONTH | SITE | CHANNEL | TYPE | YTD_REVENUE
:------------------ | :--- | ------: | :--- | ----------:
2017-01-01 00:00:00 | abc | 1 | A | 0
2017-02-01 00:00:00 | abc | 1 | A | 50
2017-03-01 00:00:00 | abc | 1 | A | 50
2017-04-01 00:00:00 | abc | 1 | A | 50
2017-05-01 00:00:00 | abc | 1 | A | 50
2017-06-01 00:00:00 | abc | 1 | A | 50
2017-07-01 00:00:00 | abc | 1 | A | 50
2017-08-01 00:00:00 | abc | 1 | A | 50
2017-09-01 00:00:00 | abc | 1 | A | 50
2017-10-01 00:00:00 | abc | 1 | A | 50
2017-11-01 00:00:00 | abc | 1 | A | 50
2017-12-01 00:00:00 | abc | 1 | A | 50
2018-01-01 00:00:00 | abc | 1 | A | 0
2018-02-01 00:00:00 | abc | 1 | A | 0
2018-03-01 00:00:00 | abc | 1 | A | 0
2018-04-01 00:00:00 | abc | 1 | A | 0
2018-05-01 00:00:00 | abc | 1 | A | 0
2018-06-01 00:00:00 | abc | 1 | A | 0
2018-07-01 00:00:00 | abc | 1 | A | 0
2018-08-01 00:00:00 | abc | 1 | A | 0
2018-09-01 00:00:00 | abc | 1 | A | 0
2018-10-01 00:00:00 | abc | 1 | A | 0
2018-11-01 00:00:00 | abc | 1 | A | 0
2018-12-01 00:00:00 | abc | 1 | A | 0
2017-01-01 00:00:00 | abc | 2 | B | 0
2017-02-01 00:00:00 | abc | 2 | B | 0
2017-03-01 00:00:00 | abc | 2 | B | 0
2017-04-01 00:00:00 | abc | 2 | B | 100
2017-05-01 00:00:00 | abc | 2 | B | 100
2017-06-01 00:00:00 | abc | 2 | B | 100
2017-07-01 00:00:00 | abc | 2 | B | 100
2017-08-01 00:00:00 | abc | 2 | B | 100
2017-09-01 00:00:00 | abc | 2 | B | 100
2017-10-01 00:00:00 | abc | 2 | B | 100
2017-11-01 00:00:00 | abc | 2 | B | 100
2017-12-01 00:00:00 | abc | 2 | B | 100
2018-01-01 00:00:00 | abc | 2 | B | 0
2018-02-01 00:00:00 | abc | 2 | B | 0
2018-03-01 00:00:00 | abc | 2 | B | 0
2018-04-01 00:00:00 | abc | 2 | B | 0
2018-05-01 00:00:00 | abc | 2 | B | 0
2018-06-01 00:00:00 | abc | 2 | B | 0
2018-07-01 00:00:00 | abc | 2 | B | 0
2018-08-01 00:00:00 | abc | 2 | B | 0
2018-09-01 00:00:00 | abc | 2 | B | 0
2018-10-01 00:00:00 | abc | 2 | B | 0
2018-11-01 00:00:00 | abc | 2 | B | 0
2018-12-01 00:00:00 | abc | 2 | B | 0
2017-01-01 00:00:00 | xyz | 1 | A | 0
2017-02-01 00:00:00 | xyz | 1 | A | 0
2017-03-01 00:00:00 | xyz | 1 | A | 0
2017-04-01 00:00:00 | xyz | 1 | A | 0
2017-05-01 00:00:00 | xyz | 1 | A | 0
2017-06-01 00:00:00 | xyz | 1 | A | 0
2017-07-01 00:00:00 | xyz | 1 | A | 0
2017-08-01 00:00:00 | xyz | 1 | A | 0
2017-09-01 00:00:00 | xyz | 1 | A | 0
2017-10-01 00:00:00 | xyz | 1 | A | 0
2017-11-01 00:00:00 | xyz | 1 | A | 0
2017-12-01 00:00:00 | xyz | 1 | A | 0
2018-01-01 00:00:00 | xyz | 1 | A | 0
2018-02-01 00:00:00 | xyz | 1 | A | 0
2018-03-01 00:00:00 | xyz | 1 | A | 0
2018-04-01 00:00:00 | xyz | 1 | A | 0
2018-05-01 00:00:00 | xyz | 1 | A | 0
2018-06-01 00:00:00 | xyz | 1 | A | 0
2018-07-01 00:00:00 | xyz | 1 | A | 0
2018-08-01 00:00:00 | xyz | 1 | A | 0
2018-09-01 00:00:00 | xyz | 1 | A | 0
2018-10-01 00:00:00 | xyz | 1 | A | 0
2018-11-01 00:00:00 | xyz | 1 | A | 0
2018-12-01 00:00:00 | xyz | 1 | A | 150
db<>fiddle here
财政年度(4 月至 3 月):
WITH calendar ( month ) AS (
SELECT ADD_MONTHS( start_month, LEVEL - 1 )
FROM (
SELECT MIN( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) ) AS start_month,
ADD_MONTHS( MAX( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) ), 11 ) AS end_month
FROM table_name
)
CONNECT BY
ADD_MONTHS( start_month, LEVEL - 1 ) <= end_month
)
SELECT TO_CHAR( ADD_MONTHS( c.month, 3 ), 'YYYY-MM' ) AS month,
t.site,
t.channel,
t.type,
SUM( COALESCE( t.revenue, 0 ) ) OVER (
PARTITION BY t.site, t.channel, t.type, TRUNC( c.month, 'YY' )
ORDER BY c.month
) AS ytd_revenue
FROM calendar c
LEFT OUTER JOIN (
SELECT ADD_MONTHS( month, -3 ) AS month,
site,
channel,
type,
revenue,
TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) AS year
FROM table_name t
) t
PARTITION BY ( site, channel, type )
ON ( c.month = t.month )
ORDER BY
site, channel, type, month;
db<>fiddle here
我想编写一个 oracle SQL 查询来计算给定维度的所有可能组合的每月 YTD 收入(累计总和)。还有一些月份没有交易,因此没有收入,在这种情况下,必须为该维度组合显示上个月 YTD 收入。给定 table:
| Month | site | channel | type | revenue |
| ----- | ---- | ------- | ---- | ------- |
| 2017-02 | abc | 1 | A | 50 |
| 2017-04 | abc | 2 | B | 100 |
| 2018-12 | xyz | 1 | A | 150 |
示例所需输出:
| Month | site | channel | type | ytd revenue |
| ----- | ---- | ------- | ---- | ------- |
| 2017-01 | abc | 1 | A | 0 |
| 2017-02 | abc | 1 | A | 50 |
| 2017-03 | abc | 1 | A | 50 |
| 2017-04 | abc | 1 | A | 50 |
| ------ | --- | -- | -- | --- |
| 2018-12 | abc | 1 | A | 1000 |
| ----- | -- | -- | -- | --- |
| 2017-04 | abc | 2 | A | 100 |
| ---- | --- | - | - | -- |
| 2018-12 | abc | 2 | A | 10 |
| --- | -- | - | - | -- |
| 2018-12 | xyz | 1 | A | 150 |
会计年度从第 1 个月开始,到第 12 个月结束。因此,对于所有维度组合,累计总和或 YTD 收入必须是每年第 1 个月到第 12 个月,如上面的示例输出所示。
如果我没理解错的话,您可以使用cross join
获取所有行,然后使用left join
和累加总和来获取最新值:
select m.month, sc.site, sc.channel, sc.type,
sum(revenue) over (partition by sc.site, sc.channel, sc.type, trunc(m.month, 'YYYY') order by m.month) as ytd_revenue
from (select distinct month from t) m cross join
(select distinct site, channel, type from t) sct left join
t
on t.month = m.month and t.site = sct.site and
t.channel = sc.channel and t.type = sct.type;
这假设所有月份都在数据中可用。如果没有,您需要生成 months 。 . .使用显式列表或使用某种生成器,例如:
with months(month) as (
select date '2019-01-01' as month
from dual
union all
select month + interval '1' month
from months
where month < date '2021-1-01'
)
使用 PARTITION OUTER JOIN
:
SELECT ADD_MONTHS( t.year, c.month - 1 ) AS month,
t.site,
t.channel,
t.type,
SUM( COALESCE( t.revenue, 0 ) ) OVER (
PARTITION BY t.site, t.channel, t.type, t.year
ORDER BY c.month
) AS ytd_revenue
FROM (
SELECT LEVEL AS month
FROM DUAL
CONNECT BY LEVEL <= 12
) c
LEFT OUTER JOIN (
SELECT t.*,
TRUNC( month, 'YY' ) AS year
FROM table_name t
) t
PARTITION BY ( site, channel, type, year )
ON ( c.month = EXTRACT( MONTH FROM t.month ) );
其中,对于示例数据:
CREATE TABLE table_name ( Month, site, channel, type, revenue ) AS
SELECT DATE '2017-02-01', 'abc', 1, 'A', 50 FROM DUAL UNION ALL
SELECT DATE '2017-04-01', 'abc', 2, 'B', 100 FROM DUAL UNION ALL
SELECT DATE '2018-12-01', 'xyz', 1, 'A', 150 FROM DUAL;
输出:
MONTH | SITE | CHANNEL | TYPE | YTD_REVENUE :------------------ | :--- | ------: | :--- | ----------: 2017-01-01 00:00:00 | abc | 1 | A | 0 2017-02-01 00:00:00 | abc | 1 | A | 50 2017-03-01 00:00:00 | abc | 1 | A | 50 2017-04-01 00:00:00 | abc | 1 | A | 50 2017-05-01 00:00:00 | abc | 1 | A | 50 2017-06-01 00:00:00 | abc | 1 | A | 50 2017-07-01 00:00:00 | abc | 1 | A | 50 2017-08-01 00:00:00 | abc | 1 | A | 50 2017-09-01 00:00:00 | abc | 1 | A | 50 2017-10-01 00:00:00 | abc | 1 | A | 50 2017-11-01 00:00:00 | abc | 1 | A | 50 2017-12-01 00:00:00 | abc | 1 | A | 50 2017-01-01 00:00:00 | abc | 2 | B | 0 2017-02-01 00:00:00 | abc | 2 | B | 0 2017-03-01 00:00:00 | abc | 2 | B | 0 2017-04-01 00:00:00 | abc | 2 | B | 100 2017-05-01 00:00:00 | abc | 2 | B | 100 2017-06-01 00:00:00 | abc | 2 | B | 100 2017-07-01 00:00:00 | abc | 2 | B | 100 2017-08-01 00:00:00 | abc | 2 | B | 100 2017-09-01 00:00:00 | abc | 2 | B | 100 2017-10-01 00:00:00 | abc | 2 | B | 100 2017-11-01 00:00:00 | abc | 2 | B | 100 2017-12-01 00:00:00 | abc | 2 | B | 100 2018-01-01 00:00:00 | xyz | 1 | A | 0 2018-02-01 00:00:00 | xyz | 1 | A | 0 2018-03-01 00:00:00 | xyz | 1 | A | 0 2018-04-01 00:00:00 | xyz | 1 | A | 0 2018-05-01 00:00:00 | xyz | 1 | A | 0 2018-06-01 00:00:00 | xyz | 1 | A | 0 2018-07-01 00:00:00 | xyz | 1 | A | 0 2018-08-01 00:00:00 | xyz | 1 | A | 0 2018-09-01 00:00:00 | xyz | 1 | A | 0 2018-10-01 00:00:00 | xyz | 1 | A | 0 2018-11-01 00:00:00 | xyz | 1 | A | 0 2018-12-01 00:00:00 | xyz | 1 | A | 150
或者,如果您想要完整的日期范围而不是每年:
WITH calendar ( month ) AS (
SELECT ADD_MONTHS( start_month, LEVEL - 1 )
FROM (
SELECT MIN( ADD_MONTHS( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ), 3 ) ) AS start_month,
ADD_MONTHS( MAX( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) ), 14 ) AS end_month
FROM table_name
)
CONNECT BY
ADD_MONTHS( start_month, LEVEL - 1 ) <= end_month
)
SELECT TO_CHAR( c.month, 'YYYY-MM' ) AS month,
t.site,
t.channel,
t.type,
SUM( COALESCE( t.revenue, 0 ) ) OVER (
PARTITION BY t.site, t.channel, t.type, TRUNC( c.month, 'YY' )
ORDER BY c.month
) AS ytd_revenue
FROM calendar c
LEFT OUTER JOIN (
SELECT t.*,
TRUNC( month, 'YY' ) AS year
FROM table_name t
) t
PARTITION BY ( site, channel, type )
ON ( c.month = t.month )
ORDER BY
site, channel, type, month;
输出:
MONTH | SITE | CHANNEL | TYPE | YTD_REVENUE :------------------ | :--- | ------: | :--- | ----------: 2017-01-01 00:00:00 | abc | 1 | A | 0 2017-02-01 00:00:00 | abc | 1 | A | 50 2017-03-01 00:00:00 | abc | 1 | A | 50 2017-04-01 00:00:00 | abc | 1 | A | 50 2017-05-01 00:00:00 | abc | 1 | A | 50 2017-06-01 00:00:00 | abc | 1 | A | 50 2017-07-01 00:00:00 | abc | 1 | A | 50 2017-08-01 00:00:00 | abc | 1 | A | 50 2017-09-01 00:00:00 | abc | 1 | A | 50 2017-10-01 00:00:00 | abc | 1 | A | 50 2017-11-01 00:00:00 | abc | 1 | A | 50 2017-12-01 00:00:00 | abc | 1 | A | 50 2018-01-01 00:00:00 | abc | 1 | A | 0 2018-02-01 00:00:00 | abc | 1 | A | 0 2018-03-01 00:00:00 | abc | 1 | A | 0 2018-04-01 00:00:00 | abc | 1 | A | 0 2018-05-01 00:00:00 | abc | 1 | A | 0 2018-06-01 00:00:00 | abc | 1 | A | 0 2018-07-01 00:00:00 | abc | 1 | A | 0 2018-08-01 00:00:00 | abc | 1 | A | 0 2018-09-01 00:00:00 | abc | 1 | A | 0 2018-10-01 00:00:00 | abc | 1 | A | 0 2018-11-01 00:00:00 | abc | 1 | A | 0 2018-12-01 00:00:00 | abc | 1 | A | 0 2017-01-01 00:00:00 | abc | 2 | B | 0 2017-02-01 00:00:00 | abc | 2 | B | 0 2017-03-01 00:00:00 | abc | 2 | B | 0 2017-04-01 00:00:00 | abc | 2 | B | 100 2017-05-01 00:00:00 | abc | 2 | B | 100 2017-06-01 00:00:00 | abc | 2 | B | 100 2017-07-01 00:00:00 | abc | 2 | B | 100 2017-08-01 00:00:00 | abc | 2 | B | 100 2017-09-01 00:00:00 | abc | 2 | B | 100 2017-10-01 00:00:00 | abc | 2 | B | 100 2017-11-01 00:00:00 | abc | 2 | B | 100 2017-12-01 00:00:00 | abc | 2 | B | 100 2018-01-01 00:00:00 | abc | 2 | B | 0 2018-02-01 00:00:00 | abc | 2 | B | 0 2018-03-01 00:00:00 | abc | 2 | B | 0 2018-04-01 00:00:00 | abc | 2 | B | 0 2018-05-01 00:00:00 | abc | 2 | B | 0 2018-06-01 00:00:00 | abc | 2 | B | 0 2018-07-01 00:00:00 | abc | 2 | B | 0 2018-08-01 00:00:00 | abc | 2 | B | 0 2018-09-01 00:00:00 | abc | 2 | B | 0 2018-10-01 00:00:00 | abc | 2 | B | 0 2018-11-01 00:00:00 | abc | 2 | B | 0 2018-12-01 00:00:00 | abc | 2 | B | 0 2017-01-01 00:00:00 | xyz | 1 | A | 0 2017-02-01 00:00:00 | xyz | 1 | A | 0 2017-03-01 00:00:00 | xyz | 1 | A | 0 2017-04-01 00:00:00 | xyz | 1 | A | 0 2017-05-01 00:00:00 | xyz | 1 | A | 0 2017-06-01 00:00:00 | xyz | 1 | A | 0 2017-07-01 00:00:00 | xyz | 1 | A | 0 2017-08-01 00:00:00 | xyz | 1 | A | 0 2017-09-01 00:00:00 | xyz | 1 | A | 0 2017-10-01 00:00:00 | xyz | 1 | A | 0 2017-11-01 00:00:00 | xyz | 1 | A | 0 2017-12-01 00:00:00 | xyz | 1 | A | 0 2018-01-01 00:00:00 | xyz | 1 | A | 0 2018-02-01 00:00:00 | xyz | 1 | A | 0 2018-03-01 00:00:00 | xyz | 1 | A | 0 2018-04-01 00:00:00 | xyz | 1 | A | 0 2018-05-01 00:00:00 | xyz | 1 | A | 0 2018-06-01 00:00:00 | xyz | 1 | A | 0 2018-07-01 00:00:00 | xyz | 1 | A | 0 2018-08-01 00:00:00 | xyz | 1 | A | 0 2018-09-01 00:00:00 | xyz | 1 | A | 0 2018-10-01 00:00:00 | xyz | 1 | A | 0 2018-11-01 00:00:00 | xyz | 1 | A | 0 2018-12-01 00:00:00 | xyz | 1 | A | 150
db<>fiddle here
财政年度(4 月至 3 月):
WITH calendar ( month ) AS (
SELECT ADD_MONTHS( start_month, LEVEL - 1 )
FROM (
SELECT MIN( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) ) AS start_month,
ADD_MONTHS( MAX( TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) ), 11 ) AS end_month
FROM table_name
)
CONNECT BY
ADD_MONTHS( start_month, LEVEL - 1 ) <= end_month
)
SELECT TO_CHAR( ADD_MONTHS( c.month, 3 ), 'YYYY-MM' ) AS month,
t.site,
t.channel,
t.type,
SUM( COALESCE( t.revenue, 0 ) ) OVER (
PARTITION BY t.site, t.channel, t.type, TRUNC( c.month, 'YY' )
ORDER BY c.month
) AS ytd_revenue
FROM calendar c
LEFT OUTER JOIN (
SELECT ADD_MONTHS( month, -3 ) AS month,
site,
channel,
type,
revenue,
TRUNC( ADD_MONTHS( month, -3 ), 'YY' ) AS year
FROM table_name t
) t
PARTITION BY ( site, channel, type )
ON ( c.month = t.month )
ORDER BY
site, channel, type, month;
db<>fiddle here