with data as (
select 1 id, 'A' name, 'fruit' r_group, '2007' year, '04' month, 5 sales from dual union all
select 2 id, 'Z' name, 'fruit' r_group, '2007' year, '04' month, 99 sales from dual union all
select 3 id, 'A' name, 'fruit' r_group, '2008' year, '05' month, 10 sales from dual union all
select 4 id, 'B' name, 'vegetable' r_group, '2008' year, '07' month, 20 sales from dual
)
select t.*,
(sum(sales) over (partition by name, r_group
order by year, month
rows between unbounded preceding and current row
) -sales ) as opening,
sum(sales) over (partition by name, r_group
order by year, month
rows between unbounded preceding and current row
) as closing
from data t
order by year , month
输出将是:
year | month | name | r_group | sales | opening | closing |
2007 | 04 | 'A' | fruit | 5 | 0 | 5 |
2007 | 04 | 'Z' | fruit | 99 | 0 | 99 |
2008 | 05 | 'A' | fruit | 10 | 5 | 15 |
2008 | 07 | 'B' | vegetable | 20 | 0 | 20 |
如果我现在在这个select语句的顶部聚合使用:
select year, month, r_group, sum(sales) sales, sum(opening) opening, sum(closing) closing from (
select t.*,
(sum(sales) over........
)
group by year, month, r_group
order by year, month
我得到以下结果:
year | month | r_group | sales | opening | closing |
2007 | 04 | fruit | 104 | 0 | 104 |
2008 | 05 | fruit | 10 | 5 | 15 |
2008 | 07 | vegetable | 20 | 0 | 20 |
错误。请注意,2008年根本没有考虑name ='Z'的值。由于累积函数向后工作,因此2008年没有name ='Z'记录向后反转。如果我在2008年设置零值记录,对于name ='Z',那么它将起作用。我想避免添加虚拟零值记录,并在查询中动态完成。如果我在数据中添加零值记录,如下所示:
select 1 id, 'A' name, 'fruit' r_group, '2007', year '04' month, 5 sales from dual union all
select 2 id, 'Z' name, 'fruit' r_group, '2007', year '04' month, 99 sales from dual union all
select 3 id, 'A' name, 'fruit' r_group, '2008', year '05' month, 10 sales from dual union all
select 4 id, 'Z' name, 'fruit' r_group, '2008', year '05' month, 0 sales from dual union all
select 5 id, 'B' name, 'vegetable' r_group, '2008', year '07' month, 20 sales from dual ))
然后第一个查询将输出:
year | month | name | r_group | sales | opening | closing |
2007 | 04 | 'A' | fruit | 5 | 0 | 5 |
2007 | 04 | 'Z' | fruit | 99 | 0 | 99 |
2008 | 05 | 'A' | fruit | 10 | 5 | 15 |
2008 | 05 | 'Z' | fruit | 0 | 99 | 99 |
2008 | 07 | 'B' | vegetable | 20 | 0 | 20 |
如果我使用第二个外部选择再次聚合,我将得到:
year | month | r_group | sales | opening | closing |
2007 | 04 | fruit | 104 | 0 | 104 |
2008 | 05 | fruit | 10 | 104 | 114 |
2008 | 07 | vegetable | 20 | 0 | 20 |
这是正确的。但是,正如我所提到的,我不想添加零值记录。这里只讨论这个主题:https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:8912311513313但是我无法做到这一点。
答案 0 :(得分:4)
一种相当简单的方法(和AskTom链接显示的方法类似)是提取所有年/月对,以及所有名称/ r_group对,然后交叉加入:
with data as (
select 1 id, 'A' name, 'fruit' r_group, '2007' year, '04' month, 5 sales from dual union all
select 2 id, 'Z' name, 'fruit' r_group, '2007' year, '04' month, 99 sales from dual union all
select 3 id, 'A' name, 'fruit' r_group, '2008' year, '05' month, 10 sales from dual union all
select 4 id, 'B' name, 'vegetable' r_group, '2008' year, '07' month, 20 sales from dual
)
select a.year, a.month, b.name, b.r_group, nvl(d.sales, 0) as sales
from (select distinct year, month from data) a
cross join (select distinct name, r_group from data) b
left join data d on d.year = a.year and d.month = a.month and d.name = b.name and d.r_group = b.r_group
order by year, month, name, r_group;
YEAR MO N R_GROUP SALES
---- -- - --------- ----------
2007 04 A fruit 5
2007 04 B vegetable 0
2007 04 Z fruit 99
2008 05 A fruit 10
2008 05 B vegetable 0
2008 05 Z fruit 0
2008 07 A fruit 0
2008 07 B vegetable 20
2008 07 Z fruit 0
但是这会产生比您想要的第一级聚合更多的行:
YEAR MO N R_GROUP SALES OPENING CLOSING
---- -- - --------- ---------- ---------- ----------
2007 04 A fruit 5 0 5
2007 04 B vegetable 0 0 0
2007 04 Z fruit 99 0 99
2008 05 A fruit 10 5 15
2008 05 B vegetable 0 0 0
2008 05 Z fruit 0 99 99
2008 07 A fruit 0 15 15
2008 07 B vegetable 20 0 20
2008 07 Z fruit 0 99 99
当与您的第二级聚合时(来自其他查询)会产生额外的行,例如,2007/04 / vegetable:
YEAR MO R_GROUP SALES OPENING CLOSING
---- -- --------- ---------- ---------- ----------
2007 04 fruit 104 0 104
2007 04 vegetable 0 0 0
2008 05 fruit 10 104 114
2008 05 vegetable 0 0 0
2008 07 fruit 0 114 114
2008 07 vegetable 20 0 20
你可以在聚合之前对它们进行部分过滤,因为所有的中间列都是零:
with data as (
select 1 id, 'A' name, 'fruit' r_group, '2007' year, '04' month, 5 sales from dual union all
select 2 id, 'Z' name, 'fruit' r_group, '2007' year, '04' month, 99 sales from dual union all
select 3 id, 'A' name, 'fruit' r_group, '2008' year, '05' month, 10 sales from dual union all
select 4 id, 'B' name, 'vegetable' r_group, '2008' year, '07' month, 20 sales from dual
)
select year,
month,
r_group,
sum(sales) sales,
sum(opening) opening,
sum(closing) closing
from (
select t.*,
(sum(sales) over (partition by name, r_group
order by year, month
rows between unbounded preceding and current row
) -sales ) as opening,
sum(sales) over (partition by name, r_group
order by year, month
rows between unbounded preceding and current row
) as closing
from (
select a.year, a.month, b.name, b.r_group, nvl(d.sales, 0) as sales
from (select distinct year, month from data) a
cross join (select distinct name, r_group from data) b
left join data d
on d.year = a.year and d.month = a.month and d.name = b.name and d.r_group = b.r_group
) t
)
where sales != 0 or opening != 0 or closing != 0
group by year, month, r_group
order by year, month;
得到:
YEAR MO R_GROUP SALES OPENING CLOSING
---- -- --------- ---------- ---------- ----------
2007 04 fruit 104 0 104
2008 05 fruit 10 104 114
2008 07 fruit 0 114 114
2008 07 vegetable 20 0 20
您可以进一步过滤该结果,以删除聚合销售值仍然为零的行,但如果您这样做,则不再需要聚合之前的过滤器;但它仍然有点凌乱。目前尚不清楚您的最外层聚合是否可以修改为这样做。
答案 1 :(得分:4)
这可以使用partitioned outer join来完成 - 但首先你必须找到不同的名称/ r_group组合,然后相应地对外连接进行分区:
with data as (select 1 id, 'A' name, 'fruit' r_group, '2007' year, '04' month, 5 sales from dual union all
select 2 id, 'Z' name, 'fruit' r_group, '2007' year, '04' month, 99 sales from dual union all
select 3 id, 'A' name, 'fruit' r_group, '2008' year, '05' month, 10 sales from dual union all
select 4 id, 'B' name, 'vegetable' r_group, '2008' year, '07' month, 20 sales from dual),
data2 as (select distinct name, r_group
from data),
res as (select d.year,
d.month,
d2.r_group,
d.id,
d2.name,
nvl(d.sales, 0) sales,
sum(nvl(d.sales, 0)) over (partition by d2.name, d2.r_group
order by d.year, d.month
rows between unbounded preceding and current row) - nvl(d.sales,0) as opening,
sum(nvl(d.sales, 0)) over (partition by d2.name, d2.r_group
order by d.year, d.month
rows between unbounded preceding and current row) as closing
from data2 d2
left outer join data d partition by (d.year, d.month) on (d.name = d2.name and d.r_group = d2.r_group))
select year,
month,
r_group,
sum(sales) sales,
sum(opening) opening,
sum(closing) closing
from res
where sales != 0
or opening != 0
or closing != 0
group by year,
month,
r_group
order by year,
month;
YEAR MONTH R_GROUP SALES OPENING CLOSING
---- ----- --------- ---------- ---------- ----------
2007 04 fruit 104 0 104
2008 05 fruit 10 104 114
2008 07 fruit 0 114 114
2008 07 vegetable 20 0 20
这与Alex的回答非常相似,但是使用分区外连接无需查找不同的年/月对,因为在join子句中会对此进行处理。