Oracle monthly totals and grand total column and row - sql

I'm trying to generate a report using Oracle which looks like this:
I got the following tables:
REGION (IDRegion, Region_Name)
SALES (IDSale, Sale_Date, Amount, IDSaleRegion)
Both tables are populated.
I cannot figure out how can I create that report by extracting each sum for the first 3 months and calculate the totals.
I've only made this script but it's not so useful:
SELECT Region_Name as Region,
COALESCE(NULL, NULL) AS "January",
COALESCE(NULL, NULL)AS"February",
COALESCE(NULL, NULL)AS"March",
COALESCE(NULL, NULL)AS"GrandTotal"
FROM REGION r INNER JOIN SALES s ON r.IDRegion=s.IDSaleRegion
GROUP BY Region_Name;
EDIT: Problem solved except the TOTAL (below Region) which calculates the totals from each Month. Any ideas how to add this row?

You can use Conditional Aggregation
select case grouping(Region_Name) when 1 then 'TOTAL' else Region_Name as "Region",
sum(case when extract(month from Sale_Date) = 1 then Amount else 0 end) AS "January",
sum(case when extract(month from Sale_Date) = 2 then Amount else 0 end) AS "February",
sum(case when extract(month from Sale_Date) = 3 then Amount else 0 end) AS "March",
sum(Amount) AS"GrandTotal"
From yourtable
Where extract(month from Sale_Date) <= 3
Group by Rollup (Region_Name);

try something like this:
SELECT Region_Name as Region,
sum(decode(extract(month from Sale_Date), 1, amount)) AS "January",
sum(decode(extract(month from Sale_Date), 2, amount)) AS"February",
sum(decode(extract(month from Sale_Date), 3, amount)) AS"March",
sum(case when extract(month from Sale_Date)<4 then amount end) AS"GrandTotal"
FROM REGION r INNER JOIN SALES s ON r.IDRegion=s.IDSaleRegion
GROUP BY Region_Name;

Related

Combine 2 queries together

I am struggling to work out combining a query that should give me 3 columns of Month, total_sold_products and drinks_sold_products
Query 1:
Select month(date), count(id) as total_sold_products
from Products
where date between '2022-01-01' and '2022-12-31'
Query 2
Select month(date), count(id) as drinks_sold_products
from Products where type = 'drinks' and date between '2022-01-01' and '2022-12-31'
I tried the union function but it summed count(id) twice and gave me only 2 columns
Many thanks!
Union is for attaching sets of data on top of each other. You need conditional aggregation or a join. See below.
SELECT MONTH(date),
COUNT(*) AS total_sold_products,
COUNT(CASE WHEN type = 'drinks' THEN 1 ELSE 0 END) AS drinks_sold_products,
FORMAT((CASE
WHEN COUNT(*) > 0 THEN
COUNT(CASE WHEN type = 'drinks' THEN 1 ELSE 0 END)/COUNT(*)
ELSE 0 END),
'P') AS Percentage
FROM Products
WHERE date BETWEEN'2022-01-01' AND '2022-12-31'
GROUP BY MONTH(date)

Turning PIVOT query into table function

Following up on BigQuery pivot on more fields, based on the suggestion by the genius Mikhail, I was wondering if the following 'pivot-like functionality' could be turned into a table function. Here would be an example:
The query from the currently accepted answer is:
select
(case when grp_set & 1 > 0 then Reseller end) as Reseller,
(case when grp_set & 2 > 0 then ProductGroup end) as ProductGroup,
(case when grp_set & 4 > 0 then Product end) as Product,
(case when grp_set & 8 > 0 then Year end) as Year,
(case when grp_set & 16 > 0 then Quarter end) as Quarter,
(case when grp_set & 32 > 0 then Product_Info end) as Product_Info,
sum(Revenue) as Revenue,
sum(Units) as Units
from `first-outlet-750.biengine_tutorial.Product`, unnest(generate_array(1, 64)) grp_set
where Year IN (2020) and Quarter in ('Q1', 'Q2')
group by 1, 2, 3, 4, 5, 6
having not (Quarter is null and Product_Info is not null)
and not (Year is null and Quarter is not null)
and not (ProductGroup is null and Product is not null)
order by 1, 2, 3, 4 , 5, 6
And I'm wondering if a table function could be created along the lines of:
PIVOT(
[row_agg1, row_agg2, ...],
[col_agg1, col_agg2, ...],
[agg_val1, agg_val2, ...]
)
So the above query could hopefully be translated into something like:
SELECT
*
FROM
PIVOT(
[Reseller, ProductGroup, Product], -- rows
[Year, Quarter, ProductInfo], -- cols
[SUM(Revenue), SUM(Units)] -- vals
)
A few things that I think might be a bit tricky:
Where does the actual table go in the FROM clause?
How would aliases work? For example, what if we wanted SUM(Revenue) to show up as TotalRev -- how to easily be able to refer to that in the main SELECT clause to be able to alias it if not just doing a SELECT * ?

Can't use column alias in GROUP BY

I can run this in mysql with no problem
SELECT
DATE_FORMAT(trans_date, '%Y-%m') month,
COUNTRY, COALESCE(COUNT(*), 0) trans_count,
COALESCE(SUM(CASE WHEN state ='approved' THEN 1 END), 0) approved_count,
COALESCE(SUM(amount), 0) trans_total_amount,
COALESCE(SUM(CASE WHEN state ='approved' THEN amount END), 0) approved_total_amount
FROM
Transactions
GROUP BY
month, COUNTRY
ORDER BY
month;
but the same query doesn't run in Orcale, I can't use GROUP BY using aggregation alias, and I can't aggregate without using GROUP BY.
I can call subquery over subquery or use CTE, but it is just so tedious.
What is a good query for type of issue?
As mentioned in another answer, You can not add aliases in GROUP BY but you can add aliases in ORDER BY. Also, DATE_FORMAT is MySql function. It is TO_CHAR in Oracle.
So your final query should be as following:
SELECT
TO_CHAR(TRANS_DATE, 'YYYY-MM') AS MONTH,
COUNTRY,
COUNT(*) AS TRANS_COUNT,
SUM(CASE WHEN STATE = 'approved' THEN 1 ELSE 0 END) AS APPROVED_COUNT,
SUM(AMOUNT) AS TRANS_TOTAL_AMOUNT,
SUM(CASE WHEN STATE = 'approved' THEN AMOUNT ELSE 0 END) AS APPROVED_TOTAL_AMOUNT
FROM TRANSACTIONS
GROUP BY TO_CHAR(TRANS_DATE, 'YYYY-MM'), COUNTRY
ORDER BY MONTH;
Oracle doesn't support aliases for the GROUP BY. Also, the COALESCE() is unnecessary in this case:
SELECT DATE_FORMAT(trans_date, '%Y-%m') as month, COUNTRY,
COUNT(*) as trans_count,
SUM(CASE WHEN state ='approved' THEN 1 ELSE 0 END) as approved_count,
SUM(amount) as trans_total_amount,
SUM(CASE WHEN state = 'approved' THEN amount ELSE 0 END) as approved_total_amount
FROM Transactions
GROUP BY DATE_FORMAT(trans_date, '%Y-%m'), COUNTRY
ORDER BY month;

How to split columns based on field title

I am working with a single warehouse table with columns like "2015 Fee", "2015 Revenue", "2016 Fee", "2016 Revenue", etc. I need to split these out into "Revenue", "Fee", and "Billed Year" in order to do some analysis. Many of the records have fee and revenue in multiple years.
The CASE statement I tried only pulls in the first year, but I need it to pull in all years.
Here's my case statements:
(CASE when 2015_revenue IS NOT NULL then 2015_revenue
when 2016_revenue_$ IS NOT NULL then 2016_revenue
END) as revenue,
(CASE when 2015_fee IS NOT NULL then 2015_fee
when 2016_fee IS NOT NULL then 2016_fee
END) as fee,
(CASE when 2015_revenue IS NOT NULL then '2015'
when 2015_fee IS NOT NULL then '2015'
when 2016_revenue IS NOT NULL then '2016'
when 2016_fee IS NOT NULL then '2016'
end) as bill_year
Any ideas?
It's not a CASE statement, it's creating a temp table with UNIONs and joining to that:
SELECT p1.[bunch of fields from table]
amts.fee
amts.revenue
amts.bill_year
FROM table p1
JOIN (SELECT id, 2015_revenue as revenue, 2015_fee as fee, '2015' as bill_year FROM table
UNION
SELECT id, 2016_revenue as revenue, 2016_fee as fee, '2016' as bill_year FROM table
U) amts on amts.id = p1.id;

Aggregation level issue on Teradata/SQL

I am currently learning SQL, as a beginner so apologies if I ask a beginner question. The database I am working on is on of a brand of shopping centers.
I have written a query to retrieve the difference in revenue per store from november to december, which seems OK to me except if I missed something.
Now I would like to adapt it to retrieve which Sku has the biggest the difference of revenue from november to december.
However the query doesn't work, and my results show no data in almost all columns, let alone the calculation column.
Here is my first query (the subquery is there only to clean the data):
SELECT TOP 5 SalesCleaned.Store AS Store, skuinfo.dept AS Dept,
store_msa.city AS City, store_msa.state as State,
SUM(CASE WHEN MonthNb=11 THEN Amount END) AS AmtNov,
SUM(CASE WHEN MonthNb=12 THEN Amount END) AS AmtDec,
SUM(CASE WHEN MonthNb=11 THEN NbOfDates END) AS DNov,
SUM(CASE WHEN MonthNb=12 THEN NbOfDates END) AS DDec,
AmtNov/DNov AS AvgNov, AmtDec/DDec AS AvgDec, (AvgDec-
AvgNov)/AvgNov*100 AS Change
FROM
Skuinfo, store_msa,
(SELECT trnsact.sku as Sku, trnsact.store AS Store, EXTRACT(MONTH from
saledate) as MonthNb, EXTRACT(Year from saledate) AS Yr, SUM(amt) AS
Amount, COUNT(DISTINCT saledate) AS NbOfDates, Amount/NbOfDates AS
AvgPStore, CASE WHEN (Yr=2005 AND MonthNb=8) THEN 'exclude' ELSE
'include' END AS UseDate
FROM trnsact
WHERE(UseDate='include') AND (stype='P')
GROUP BY MonthNb, Store, Yr, stype, Sku
HAVING(NbOfDates>=20)) AS SalesCleaned
WHERE SalesCleaned.store=store_msa.store AND
skuinfo.sku=SalesCleaned.sku
GROUP BY SalesCleaned.Store, City, State, Dept
ORDER BY Change DESC;
And here is the 2nd query, which returns a result wiht a lot of data mmissing, and which I have no idea how to fix:
SELECT SalesCleaned.sku AS Sku,
SUM(CASE WHEN MonthNb=11 THEN Amount END) AS AmtNov,
SUM(CASE WHEN MonthNb=12 THEN Amount END) AS AmtDec,
(AmtDcb - AmtNov)/ AmtDcb *100 AS Change
FROM
(SELECT trnsact.sku AS Sku, trnsact.store AS Store, EXTRACT(MONTH from
saledate) as MonthNb, EXTRACT(Year from saledate) AS Yr, SUM(amt) AS
Amount, COUNT(DISTINCT saledate) AS NbOfDates, Amount/NbOfDates AS
AvgPStore, CASE WHEN (Yr=2005 AND MonthNb=8) THEN 'exclude' ELSE
'include' END AS UseDate
FROM trnsact
WHERE(UseDate='include') AND (stype='P')
GROUP BY Sku, Store, MonthNb, Store, Yr, stype) AS SalesCleaned
GROUP BY Sku, MonthNb, Amount, NbOfDates
HAVING NbOfDates>=20
ORDER BY Change DESC;
Any feedback welcome