I have the below table with 1 amount column and few other columns.
Input
Country | Zone | dept | accnt | amount |
---|---|---|---|---|
IND | North | CS | A | 100 |
IND | West | IT | B | 200 |
IND | West | IT | D | 500 |
US | East | IT | C | 300 |
I want to find the below.
Output
Country | tot_amnt | Zone | accnt |
---|---|---|---|
IND | 800 | West | D |
US | 300 | East | C |
Code I wrote
with tot_rev as (select country,sum(amount)as net_amnt from sales group by country),
max_zone as (
select * from
(
select a.country,a.zone,a.sum1,rank()over(PARTITION BY country order by a.sum1 desc)as rnk from
(
select distinct country,Zone,sum(amount)over(PARTITION BY country,zone)as sum1 from Sales
)a)b
where b.rnk=1
),
max_accnt as ( select * from
(
select a.country,a.accnt,a.sum1,rank()over(PARTITION BY country order by a.sum1 desc)as rnk from
(
select distinct country,accnt,sum(amount)over(PARTITION BY country,accnt)as sum1 from Sales
)a)b
where b.rnk=1)
select tot_rev.country,tot_rev.net_amnt,max_zone.zone,max_accnt.accnt from
tot_rev inner join max_zone
on tot_rev.country=max_zone.country
inner join max_accnt
on max_zone.country=max_accnt.country
Though the code I wrote gives correct results, please let me know if there is a better more efficient way of writing this. I am dealing with a around 500 million records.
You can use aggregation with the KEEP
clause:
SELECT Country,
SUM( amount ) AS total_amount,
MAX( Zone ) KEEP ( DENSE_RANK LAST ORDER BY amount ) AS Zone,
MAX( Accnt ) KEEP ( DENSE_RANK LAST ORDER BY amount ) AS Accnt
FROM sales
GROUP BY Country;
Which, for the sample data:
CREATE TABLE sales ( Country, Zone, dept, accnt, amount ) AS
SELECT 'IND', 'North', 'CS', 'A', 100 FROM DUAL UNION ALL
SELECT 'IND', 'West', 'IT', 'B', 200 FROM DUAL UNION ALL
SELECT 'IND', 'West', 'IT', 'D', 500 FROM DUAL UNION ALL
SELECT 'IND', 'South', 'ZZ', 'E', 600 FROM DUAL UNION ALL
SELECT 'US', 'East', 'IT', 'C', 300 FROM DUAL;
Outputs:
COUNTRY | TOTAL_AMOUNT | ZONE | ACCNT:------ | -----------: |:---- |:---- IND | 1400 | South | E US | 300 | East | C
If you want the Zone
with the highest total (rather than the highest individual amount):
SELECT Country,
SUM( amount ) AS total_amount,
MAX( Zone ) KEEP ( DENSE_RANK LAST ORDER BY zone_total_amount ) AS Zone,
MAX( Accnt ) KEEP ( DENSE_RANK LAST ORDER BY amount ) AS Accnt
FROM (
SELECT s.*,
SUM( amount ) OVER ( PARTITION BY Country, Zone ) AS zone_total_amount
FROM sales s
)
GROUP BY Country;
or
SELECT Country,
SUM( zone_total_amount ) AS total_amount,
MAX( Zone ) KEEP ( DENSE_RANK LAST ORDER BY zone_total_amount ) AS Zone,
MAX( Accnt ) KEEP ( DENSE_RANK LAST ORDER BY max_amount ) AS Accnt
FROM (
SELECT Country,
Zone,
MAX( accnt ) KEEP ( DENSE_RANK LAST ORDER BY Amount ) AS Accnt,
MAX( amount ) AS max_amount,
SUM( amount ) AS zone_total_amount
FROM sales s
GROUP BY Country, Zone
)
GROUP BY Country;
Which both output:
COUNTRY | TOTAL_AMOUNT | ZONE | ACCNT:------ | -----------: |:--- |:---- IND | 1400 | West | E US | 300 | East | C
db<>fiddle here
I would do something like this. Notice that the subquery uses rollup aggregation, including the grouping_id
identifier. https://oracle-base.com/articles/misc/rollup-cube-grouping-functions-and-grouping-sets#grouping_id
select country, min(case gid when 3 then amount end) as amount,
min(zone) keep (dense_rank last
order by decode(gid, 1, amount) nulls first) as zone,
min(accnt) keep (dense_rank last
order by decode(gid, 0, amount) nulls first) as accnt
from (
select country, zone, accnt,
grouping_id(zone, accnt) as gid, sum(amount) as amount
from sales
group by country, rollup (zone, accnt)
)
group by country
;
COUNTRY AMOUNT ZONE ACCNT
------- ---------- ----- -----
IND 1400 West E
US 300 East C
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.