I am needing to create two rows that contain Totals as opposed to the typical one totals row. Grand Totals and Averages.
I am creating a report using basic SQL and I am working out of an Oracle database but I am not using any PL/SQL.
I'm currently using Group By Grouping Sets to produce a report and one row is the row that contains the Grand Totals. These totals are currently being produced using SUM(column) using a mix of aggregate and analytical functions to produce my one row of Grand Totals. What I need is another row that produces totals on the same data set. What is the best way to accomplish this? When I say best I'm thinking of the load on my database as this report will be running against a ton of data. My examples are extremely basic but get the point across.
Below is some sample data that produces Summed Grand Totals using Group By Grouping Sets. What's missing is another row below the Grand Totals that I would like to produce Averages.
WITH sample_data AS
(
SELECT 1 AS client_key, 'NASA' AS client, 8 AS SPACESHIPS_SOLD, 105585 AS REVENUE FROM DUAL UNION ALL
SELECT 2 AS client_key, 'Origin' AS client, 3 AS SPACESHIPS_SOLD, 36581 AS REVENUE FROM DUAL UNION ALL
SELECT 3 AS client_key, 'SpaceX' AS client, 7 AS SPACESHIPS_SOLD, 83851 AS REVENUE FROM DUAL
)
SELECT sd.client_key
, CASE WHEN grouping(sd.client) = 0 THEN to_char(sd.client) ELSE 'Grand Totals -->' END AS client
, SUM(sd.spaceships_sold) AS spaceships_sold
, SUM(sd.revenue) AS revenue
FROM sample_data sd
GROUP BY
GROUPING SETS (
(sd.client_key, sd.client),
()
)
;
Example Image of what I'm looking for.
Below are my thoughts as to how I can get this extra Totals Row but not sure if this is what I should be doing in order to obtain this. Seems convoluted and I keep thinking that this should be an existing feature of Grouping Sets. In the below approach I'm using CTE's and UNION ALL to get the extra Averages Totals at the bottom of my data set as seen in the screenshot below.
SQL from the above screenshot.
WITH sample_data AS
(
SELECT 1 AS client_key, 'NASA' AS client, 8 AS SPACESHIPS_SOLD, 105585 AS REVENUE FROM DUAL UNION ALL
SELECT 2 AS client_key, 'Origin' AS client, 3 AS SPACESHIPS_SOLD, 36581 AS REVENUE FROM DUAL UNION ALL
SELECT 3 AS client_key, 'SpaceX' AS client, 7 AS SPACESHIPS_SOLD, 83851 AS REVENUE FROM DUAL
)
, data_Sum_totals AS
(
SELECT sd.client_key
, CASE WHEN grouping(sd.client) = 0 THEN to_char(sd.client) ELSE 'Grand Totals -->' END AS client
, SUM(sd.spaceships_sold) AS spaceships_sold
, SUM(sd.revenue) AS revenue
FROM sample_data sd
GROUP BY
GROUPING SETS (
(sd.client_key, sd.client),
()
)
)
, data_Avg_totals AS
(
SELECT grouping(sd.client_key) AS row_group
, sd.client_key
, CASE WHEN grouping(sd.client) = 0 THEN to_char(sd.client) ELSE 'AVG Totals -->' END AS client
, AVG(sd.spaceships_sold) AS spaceships_sold
, AVG(sd.revenue) AS revenue
FROM sample_data sd
GROUP BY
GROUPING SETS (
(sd.client_key, sd.client),
()
)
HAVING grouping(sd.client_key) = 1 /* This line restricts the output to only give me the Totals row */
)
SELECT client_key, client, spaceships_sold, revenue
FROM data_Sum_totals
UNION ALL
SELECT client_key, client, spaceships_sold, revenue
FROM data_Avg_totals
;
You pointed out :
I keep thinking that this should be an existing feature of Grouping Sets. In the below approach I'm using CTE's and UNION ALL to get the extra Averages Totals at the bottom of my data set as seen in the screenshot below
and How the [grouping-sets]
tag is defined :
The GROUPING SETS operator is an extensions of the GROUP BY clause. It can generate the same result set as when you use UNION ALL to combine single grouping queries; however, using GROUPING SETS operator is usually more efficient.
Therefore, you had such a nice approach.
I think using GROUPING_ID
suits best for your case as in the following SQL statement :
SELECT client_key,
CASE WHEN flag = 3 THEN 'AVG Totals -.->'
WHEN flag = 2 THEN 'Grand Totals -.->'
ELSE client
END AS client ,
SUM(spaceships_sold)/ DECODE(flag,3,3,1) AS spaceships_sold,
SUM(revenue)/ DECODE(flag,3,3,1) AS revenue
FROM
(
WITH sample_data AS
(
SELECT 1 AS client_key, 'NASA' AS client, 8 AS SPACESHIPS_SOLD, 105585 AS REVENUE FROM DUAL
UNION ALL
SELECT 2 AS client_key, 'Origin' AS client, 3 AS SPACESHIPS_SOLD, 36581 AS REVENUE FROM DUAL
UNION ALL
SELECT 3 AS client_key, 'SpaceX' AS client, 7 AS SPACESHIPS_SOLD, 83851 AS REVENUE FROM DUAL
)
SELECT sd.client_key,
nvl2(sd.client_key,client,null) AS client
, SUM(sd.spaceships_sold) AS spaceships_sold
, SUM(sd.revenue) AS revenue
, GROUPING_ID(sd.client_key, sd.client) AS flag
FROM sample_data sd
GROUP BY
GROUPING SETS (
(sd.client_key, sd.client),
(sd.client),()
)
)
GROUP BY client_key, flag, client
ORDER BY client_key, revenue desc;
CLIENT_KEY CLIENT SPACESHIPS_SOLD REVENUE
----------- ---------------- --------------- --------
1 NASA 8 105585
2 Origin 3 36581
3 SpaceX 7 83851
NULL Grand Totals -.-> 18 226017
NULL AVG Totals -.-> 6 75339
Update to SQL to work with any number or records aka clients
SELECT client_key,
CASE WHEN flag = 3 THEN 'AVG Totals -->'
WHEN flag = 2 THEN 'Grand Totals -->'
ELSE client
END AS client
, flag,
SUM(spaceships_sold)/ DECODE(flag,3,tot_clients,1) AS spaceships_sold,
SUM(revenue)/ DECODE(flag,3,tot_clients,1) AS revenue
FROM
(
WITH sample_data AS
(
SELECT 1 AS client_key, 'NASA' AS client, 8 AS SPACESHIPS_SOLD, 105585 AS REVENUE FROM DUAL
UNION ALL
SELECT 2 AS client_key, 'Origin' AS client, 3 AS SPACESHIPS_SOLD, 36581 AS REVENUE FROM DUAL
UNION ALL
SELECT 3 AS client_key, 'SpaceX' AS client, 7 AS SPACESHIPS_SOLD, 83851 AS REVENUE FROM DUAL
UNION ALL
SELECT 4 AS client_key, 'Comp' AS client, 4 AS SPACESHIPS_SOLD, 95823 AS REVENUE FROM DUAL
UNION ALL
SELECT 4 AS client_key, 'CNSA' AS client, 11 AS SPACESHIPS_SOLD, 135851 AS REVENUE FROM DUAL
)
SELECT sd.client_key,
nvl2(sd.client_key,client,null) AS client
, SUM(sd.spaceships_sold) AS spaceships_sold
, SUM(sd.revenue) AS revenue
, COUNT(sd.client_key) AS tot_clients
, GROUPING_ID(sd.client_key, sd.client) AS flag
FROM sample_data sd
GROUP BY
GROUPING SETS (
(sd.client_key, sd.client),
(sd.client),()
)
)
GROUP BY client_key, flag, client, tot_clients
ORDER BY client_key, revenue desc
;