Reputation: 503
I need to create a dynamic grouping table in SQL that would look similar to this:
Customer Rating Asset Type Exposure
ABC 9- apples -10
bananas 20
subtotal 10
DEF 5 grapes 5
GHI 8+ apples 15
bananas 9
subtotal 25
I have tried pivots and left joins but i am getting what i need.
Code
SELECT
A.CUSTOMER
,A.FRR as Rating
,C.PRODUCT_TYPE
,SUM(D.UTILIZATION) as Exposure
from table1 A
LEFT JOIN table1 B ON A.CUSTOMER =
B.CUSTOMER and A.PRODUCT_TYPE = 'Apples'
LEFT JOIN table1 C ON A.CUSTOMER =
C.CUSTOMER and A.PRODUCT_TYPE = 'Bananas'
LEFT JOIN table1 D ON A.CUSTOMER =
D.CUSTOMERand A.PRODUCT_TYPE = 'Grapes'
GROUP BY A.CUSTOMER
,A.FRR
,C.PRODUCT_TYPE
;
any help/direction you can provide would be great!
Upvotes: 0
Views: 465
Reputation: 65373
You can use lag()
analytic functions with rollup(customer, frr, product_type)
:
with t1 as
(
select customer, frr, product_type, sum(utilization) as exposure,
lag(customer) over (order by customer,product_type) as lg_cst,
lag(frr) over (order by customer,product_type) as lg_frr
from table1
group by rollup(customer, frr, product_type)
), t2 as
(
select
case when nvl(lg_cst,'') = customer
then
null
else
customer
end as customer,
case when nvl(lg_frr,'') = frr
then
null
else
frr
end as frr2,
case when nvl(lg_frr,'') = frr and product_type is null and customer is not null then
'subtotal'
when customer is null then
'Grand Total'
else
product_type
end as product_type, exposure
from t1
)
select *
from t2
where product_type is not null;
Upvotes: 0
Reputation: 74690
Oracle GROUP BY supports ROLLUP and GROUPING SETS that will provide the broader aggregations for you:
So your base query looks like:
SELECT
...
GROUP BY
A.CUSTOMER,A.FRR,C.PRODUCT_TYPE
And it produces:
Customer Rating Asset Type Exposure
ABC 9- apples -10
ABC 9- bananas 20
DEF 5 grapes 5
DEF 5 apples 15
DEF 5 bananas 9
If you change it to group by a grouping set:
SELECT
...
GROUP BY GROUPING SETS (
(A.CUSTOMER,A.FRR,C.PRODUCT_TYPE),(A.CUSTOMER,A.FRR)
)
You'll get extra rows that subtotal the fruit, and the fruit name is NULL to indicate it's a total (Actually the GROUPING() function indicates if it's "null because it's a total" or "null because the underlying data really is null" but..):
Customer Rating Asset Type Exposure
ABC 9- apples -10
ABC 9- bananas 20
ABC 9- NULL 10 <-- total of fruit for ABC/9-
DEF 5 grapes 5
DEF 5 apples 15
DEF 5 bananas 9
DEF 5 NULL 29 <-- total of fruit for DEF/5
GROUP BY
ROLLUP(A.CUSTOMER,A.FRR,C.PRODUCT_TYPE)
would do a similar thing, except it goes all the way up to the root from right to left (you get totals rows like ABC NULL NULL 45
, DEF NULL NULL 62
and NULL NULL NULL 107
Footnote: I've made some assumptions that your example data has errors - I couldn't see how 5+15+9 added up to 25, or whether DEF and GHI should be the same etc. Finally, I wouldn't attempt to hide data on rows if it's the same as the row above, because that then means the only thing keeping the data set meaningful is the order. It's OK to do in the front end just before the user sees it, but doing it in a back end, where it might be passed aropund several places before it's used, means any one of those places could lose the order and wreck the meaning of the data. For more discussion, see a very similar answer I just made here: Get count of particular category and further bifurcated sub category using SQL query
If you're still desperate to hide data in column X on row N that is the same as data in column X of row N-1, you can use the LAG analytic function to pull the previous row data and make a decision on whether to null it if it's the same as this row's data
You asked about not totalling if there was only one row anyway. In these circumstances we could perhaps do something like count the number of detail rows and exclude those rows where it's a totals row and the number of details rows it summarises is 1
To do this add the following line to the end of the query:
HAVING NOT (GROUPING(PRODUCT_TYPE) = 1 and COUNT(DISTINCT PRODUCT_TYPE) = 1)
For more on this, see the db fiddle: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=935992f52f067c84b2899e2acea688a0
Essentially how it works is:
GROUPING(PRODUCT_TYPE)
returns 0 for a details row and 1 for a subtotal rowCOUNT(DISTINCT PRODUCT_TYPE)
returns 1 for a details row and N for a totals row where N is the number of distinct assets that were rolled into that total:
By excluding the rows where the count of distinct asset types was 1 AND the row is a totals row, we suppress totals rows that essentially repeat the details row
Upvotes: 1