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
Related
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;
I have a database table that captures every Sales Transaction:
Transactions
(
ID INT,
TransactionDate DATETIME,
SalesAmount MONEY
)
I need to run a T-SQL query to produce a Sales Report, that shows :
(1) Sales (monthly)
(2) Cumulative Monthly Sales
These will be two columns per year; PIVOT the results for next year on next two neighbor columns (structure as ExcelSheet template below). The results will extend horizontally for all Years in the transaction data.
You could use conditional aggegation and window functions, as follows:
select
datename(month, TransactionDate),
sum(case when year(TransactionDate) = 2017 then SalesAmount end) [Sales 2017],
sum(sum(case when year(TransactionDate) = 2017 then SalesAmount end))
over(order by month(TransactionDate)) [Cumulative 2017],
sum(case when year(TransactionDate) = 2018 then SalesAmount end) [Sales 2018],
sum(sum(case when year(TransactionDate) = 2018 then SalesAmount end))
over(order by month(TransactionDate)) [Cumulative 2018],
sum(case when year(TransactionDate) = 2019 then SalesAmount end) [Sales 2019],
sum(sum(case when year(TransactionDate) = 2019 then SalesAmount end))
over(order by month(TransactionDate)) [Cumulative 2019],
from Transactions
group by month(TransactionDate), datename(month, TransactionDate)
order by month(TransactionDate)
I am querying two tables from an oracle database. POS.SNAPSHOT_SUMMARY_LOG contains daily snapshots of all reserved orders from today into the future. POS.SALES_SUMMARY_LOG contains final sales history until yesterday. I am trying to create a denormalized table that has a column with sales figures at different points in time (i.e. WINDOWS). The query below works well and is fast. I get a column for each sum for each window. See results for 2/26/18-3/26/18 windows_query. (The PREVIOUS_YEAR__DATE column is the most similar weekday from the previous year and not the same calendar date of the previous year)
However, I can't figure out how to group by the dimensions of PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL to the results so that the query runs quickly.
WITH SNAP AS( SELECT
TRUNC(SYSDATE,'DDD')-SNAPSHOT_DATE AS WINDOW, CURRENT_YEAR_DATE, PREVIOUS_YEAR__DATE, PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL, QUANTITY, REVENUE
FROM POS.SNAPSHOT_SUMMARY_LOG
WHERE (SNAPSHOT_DATE IN (TRUNC(SYSDATE,'DDD'), TRUNC(SYSDATE,'DDD')-7, TRUNC(SYSDATE,'DDD')-365,TRUNC(SYSDATE,'DDD')-372))),
HIST AS (SELECT CURRENT_YEAR_DATE, PREVIOUS_YEAR__DATE, PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL, QUANTITY, REVENUE
FROM POS.SALES_SUMMARY_LOG)
SELECT DISTINCT SNAP.CURRENT_YEAR_DATE, SNAP.PREVIOUS_YEAR__DATE,
CONCAT(SNAP0.REVENUE,HIST0.REVENUE)+0 AS REVENUE0,
CONCAT(SNAP7.REVENUE,HIST7.REVENUE)+0 AS REVENUE7,
CONCAT(SNAP365.REVENUE,HIST365.REVENUE)+0 AS REVENUE365,
CONCAT(SNAP372.REVENUE,HIST372.REVENUE)+0 AS REVENUE372,
CONCAT(SNAP0.QUANTITY,HIST0.QUANTITY)+0 AS QUANTITY0,
CONCAT(SNAP7.QUANTITY,HIST7.QUANTITY)+0 AS QUANTITY7,
CONCAT(SNAP365.QUANTITY,HIST365.QUANTITY)+0 AS QUANTITY365,
CONCAT(SNAP372.QUANTITY,HIST372.QUANTITY)+0 AS QUANTITY372
FROM SNAP
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=0 GROUP BY CURRENT_YEAR_DATE) SNAP0 ON SNAP.CURRENT_YEAR_DATE=SNAP0.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=7 GROUP BY CURRENT_YEAR_DATE) SNAP7 ON SNAP.CURRENT_YEAR_DATE=SNAP7.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=365 GROUP BY CURRENT_YEAR_DATE ) SNAP365 ON SNAP.PREVIOUS_YEAR_DATE=SNAP365.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=372 GROUP BY CURRENT_YEAR_DATE) SNAP372 ON SNAP.PREVIOUS_YEAR_DATE=SNAP372.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-0 GROUP BY CURRENT_YEAR_DATE) HIST0 ON SNAP.CURRENT_YEAR_DATE=HIST0.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-7 GROUP BY CURRENT_YEAR_DATE) HIST7 ON SNAP.CURRENT_YEAR_DATE=HIST7.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-365 GROUP BY CURRENT_YEAR_DATE) HIST365 ON SNAP.PREVIOUS_YEAR_DATE=HIST365.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-372 GROUP BY CURRENT_YEAR_DATE) HIST372 ON SNAP.PREVIOUS_YEAR_DATE=HIST372.CURRENT_YEAR_DATE
WHERE SNAP.CURRENT_YEAR_DATE BETWEEN TRUNC(SysDate,'YEAR') AND ADD_MONTHS(TRUNC(TRUNC(SysDate,'YEAR'),'Y'),15)
I think you can rewrite your query using conditional aggregation, which ought to be a lot more performant than your current query.
E.g. I think you could rewrite it to be something like:
SELECT current_year_date,
previous_year__date,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 0 THEN revenue END) snap_revenue0,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 7 THEN revenue END) snap_revenue7,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 365 THEN revenue END) snap_revenue365,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 372 THEN revenue END) snap_revenue372,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 0 THEN revenue END) hist_revenue0,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 7 THEN revenue END) hist_revenue7,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 365 THEN revenue END) hist_revenue365,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 372 THEN revenue END) hist_revenue372,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 0 THEN quantity END) snap_quantity0,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 7 THEN quantity END) snap_quantity7,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 365 THEN quantity END) snap_quantity365,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 372 THEN quantity END) snap_quantity372,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 0 THEN quantity END) hist_quantity0,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 7 THEN quantity END) hist_quantity7,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 365 THEN quantity END) hist_quantity365,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 372 THEN quantity END) hist_quantity372
FROM pos.snapshot_summary_log
GROUP BY current_year_date,
previous_year__date;
I don't quite understand what you mean by "add the dimensions of PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL"; if you mean you want the results grouped with those additional columns, then it's simply a matter of adding them into the above select column and group by lists, e.g.:
SELECT current_year_date,
previous_year__date,
product_category,
customer_category,
sales_channel,
<SUM columns>
FROM pos.snapshot_summary_log
GROUP BY current_year_date,
previous_year__date,
product_category,
customer_category,
sales_channel;
I've not done anything with the columns; you may want to throw an outer query around this that adds the snap and hist columns together, or maybe COALESCEs them, but I'll leave that up to you to decide.
>SELECT sku, jun_sale, jul_sale, aug_sale, (jun_sale + jul_sale + aug_sale)
>AS total_sale,
>CASE
>when EXTRACT(MONTH from saledate)=6 AND stype= 'P'
>then sum(amt)
>end as jun_sale,
>CASE
>when EXTRACT(MONTH from saledate)=7 AND stype= 'P'
>then sum(amt)
>end as jul_sale,
>CASE
>when EXTRACT(MONTH from saledate)=8 AND stype= 'P'
>then sum(amt)
>end as aug_sale
>FROM trnsact
>group by 1
>order by total_sale;
Selected non-aggregate values must be part of the associated group
Can someone find out what is the error in the case?
You probably want to SUM over CASE:
select sku,
sum(case
when EXTRACT(MONTH from saledate)=6 AND stype= 'P'
then amt
end) as jun_sale,
sum(case
when EXTRACT(MONTH from saledate)=7 AND stype= 'P'
then amt
end) as jul_sale,
sum(case
when EXTRACT(MONTH from saledate)=8 AND stype= 'P'
then amt
end) as aug_sale,
jun_sale + jul_sale + aug_sale as total_sale
from trnsact
group by 1
order by total_sale;
And you should add a WHERE-condition restricting to rows from June to August.
Edit:
If an item was not sold in any of the three months you will get a NULL (and then the total_sale calculation will also result in NULL). You can add else 0 to each CASE to get zeroes instead.
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;