CTE Rolling 3 Mo Avg - sql

output for all 3 queries
working on an assigment, below is the ask, she has directed us to use a CTE
Write SQL query code used to explore the database tables and write a query that retrieves finance amounts from "FactFinance" in the "AdventureWorksDW2016CTP3" database and returns those amounts, organized by month, and showing a 3-month rolling average
SELECT DateKey,
month(date) as [Month],
year(date) as [Year],
SUM ( ALL Amount) OVER (PARTITION BY Date ORDER BY Date ASC) AS Amount
FROM FactFinance
SELECT
YEAR(Date) AS Year,
MONTH(Date) AS Month,
SUM(Amount) AS Amount
FROM FactFinance
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY Year, Month;
WITH CTE AS (
SELECT
DateKey AS Month,
AVG(Amount) AS AvgAmt
from FactFinance
group by DateKey
)
SELECT
Month,
AvgAmt
FROM CTE
GO
oUTPUT for last query Needing 3 month rolling average

First, you should know the right way to answer this. Assuming you have data for all three months, then:
SELECT YEAR(Date) AS Year,
MONTH(Date) AS Month,
SUM(Amount) AS Amount,
AVG(SUM(Amount)) OVER (ORDER BY MIN(DATE)
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as rolling_3month_avg
FROM FactFinance
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY Year, Month;
If I were told to use a CTE for this, I might be tempted to do:
WITH unnecessary_cte as (
SELECT YEAR(Date) AS Year,
MONTH(Date) AS Month,
SUM(Amount) AS Amount,
AVG(SUM(Amount)) OVER (ORDER BY MIN(DATE)
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as rolling_3month_avg
FROM FactFinance
GROUP BY YEAR(Date), MONTH(Date)
)
SELECT *
FROM unnecessary_cte
ORDER BY YEAR, MONTH;
However, we can try to read your instructor's mind and speculate that she wants you to write something like this:
WITH ym as (
SELECT YEAR(Date) AS Year,
MONTH(Date) AS Month,
SUM(Amount) AS Amount
FROM FactFinance
GROUP BY YEAR(Date), MONTH(Date)
)
SELECT ym.*,
(SELECT AVG(Amount)
FROM ym ym2
WHERE 12 * ym2.year + ym2.month
BETWEEN 12 * ym.year + ym.month - 2 AND
12 * ym.year + ym.month
) as rolling_3month_avg
FROM ym
ORDER BY YEAR, MONTH;

Related

In pgAdmin for postgreSQL, I am unable to query for MAXimum rows from another query that SUMs up and sorts rows. Working with 1 table

This is the initial query that groups, sums up, and orders the busiest day of the week per month and year for a small retail store:
SELECT year, month, day_of_week, SUM(total_revenue)
FROM vip_sales
GROUP BY year, month, day_of_week
ORDER BY year, month, SUM DESC
and returns the table in attached image. And that is what I want to see INITIALLY.
Now I want to do a query on this result (image) that only shows the MAX sums of each month - essentially ONLY the rows that I circled, which is the best day (highest SUM) is each of the months of January (1) , February(2), ...
I tried the following:
SELECT year, month, day_of_week, MAX(SUM(total_revenue))
FROM vip_sales
GROUP BY year, month, day_of_week
ORDER BY year, month
But I got this error:
ERROR: aggregate function calls cannot be nested
LINE 1: SELECT year, month, day_of_week, MAX(SUM(total_revenue))
^
SQL state: 42803
Character: 38
Then I tried:
SELECT year, month, day_of_week, MAX(SUM)
FROM
(SELECT year, month, day_of_week, SUM(total_revenue)
FROM vip_sales
GROUP BY year, month, day_of_week
ORDER BY year, month, SUM DESC)
ORDER BY year, month
And I got another error with hint:
ERROR: subquery in FROM must have an alias
LINE 3: (SELECT year, month, day_of_week, SUM(total_revenue)
^
HINT: For example, FROM (SELECT ...) [AS] foo.
SQL state: 42601
Character: 51
So then I tried:
SELECT year, month, day_of_week, MAX(SUM)
FROM
(SELECT year, month, day_of_week, SUM(total_revenue)
FROM vip_sales
GROUP BY year, month, day_of_week
ORDER BY year, month, SUM DESC) AS foo
GROUP BY foo.year, foo.month, foo.day_of_week
ORDER BY foo.year, foo.month, MAX DESC
AND
SELECT foo.year, foo.month, foo.day_of_week, MAX(foo.SUM)
FROM
(SELECT year, month, day_of_week, SUM(total_revenue)
FROM vip_sales
GROUP BY year, month, day_of_week
ORDER BY year, month, SUM DESC) AS foo
GROUP BY foo.year, foo.month, foo.day_of_week
ORDER BY foo.year, foo.month, MAX DESC
But they are redundant and both return the SAME results as in the image - all days of the week in that month, and NOT the day of the week which is the day with maximum sales in that month in that year.
I googled 'nested queries' and 'sub queries" but I tried some techniques but got errors with no hints. I am not finding anything that logically explains how to do SUM and then query the MAXIMUM of the SUMs.
Any suggestions?
You can use ROW_NUMBER() to create a custom partition
SELECT year, month, day, thesum
FROM (
SELECT year, month, day, thesum,
ROW_NUMBER() OVER (PARTITION BY year, month ORDER BY thesum DESC) RN
FROM (
SELECT year, month, day_of_week, SUM(total_revenue) as thesum
FROM vip_sales
GROUP BY year, month, day_of_week
--ORDER BY year, month, SUM DESC
) x
) y
WHERE RN = 1

How to retrieve last year data on line-by-line basis (main set grouped by year, month & aggregated on volume)?

Is there a way to easily retrieve last years data during volume aggregation, grouped by year, month.
Sample of code below (from BQ). It shows an error in the subquery
WHERE clause expression references t1.date which is neither grouped nor aggregated
SELECT
EXTRACT(YEAR FROM t1.date) AS year,
EXTRACT(MONTH FROM t1.date) AS month,
t1.ProductId AS product,
SUM(t1.Quantity) AS UnitsSold_TY,
(SELECT
SUM(Quantity)
FROM `new-project-jun21.sales.sales_info`
WHERE
EXTRACT(YEAR FROM date) = EXTRACT(YEAR FROM t1.date) - 1 AND
EXTRACT(MONTH FROM date) = EXTRACT(MONTH FROM t1.date) AND
ProductId = t1.ProductId
GROUP BY
EXTRACT(YEAR FROM date),
EXTRACT(MONTH FROM date),
EXTRACT(MONTH FROM t1.date),
ProductId) AS UnitsSold_LY
FROM `new-project-jun21.sales.sales_info` AS t1
GROUP BY
year,
month,
product
ORDER BY product, year, month
If you have data every month, then you can use lag(). I would recommend using date_trunc() instead of separating the year and date components. So:
SELECT productId,
DATE_TRUNC(date, INTERVAL 1 MONTH) as yyyymm
SUM(Quantity),
LAG(SUM(Quantity), 12) OVER (PARTITION BY ProductId ORDER BY MIN(date)) as yoy
FROM `new-project-jun21.sales.sales_info`
GROUP BY product_id, DATE_TRUNC(date, INTERVAL 1 MONTH);
ORDER BY product, yyyymm;
If you have missing months for some products, then you can still use window functions, but the logic is a bit more complicated.
EDIT:
If you don't have data every month, then you can use a RANGE specification, but you need a month counter:
SELECT productId,
DATE_TRUNC(date, INTERVAL 1 MONTH) as yyyymm
SUM(Quantity),
MAX(SUM(Quantity)) OVER (PARTITION BY ProductId
ORDER BY DATE_DIFF(MIN(DATE), DATE '2000-01-01', month)
RANGE BETWEEN 12 PRECEDING AND 12 PRECEDING
) as yoy
FROM `new-project-jun21.sales.sales_info`
GROUP BY product_id, DATE_TRUNC(date, INTERVAL 1 MONTH);
ORDER BY product, yyyymm;
Functionally, this should be equivalent, but I'd probably try to avoid the correlated subquery:
WITH xrows AS (
SELECT EXTRACT(YEAR FROM t1.date) AS year
, EXTRACT(MONTH FROM t1.date) AS month
, t1.ProductId AS product
, SUM(t1.Quantity) AS UnitsSold_TY
FROM `new-project-jun21.sales.sales_info` AS t1
GROUP BY year, month, product
)
SELECT t.*
, ( SELECT SUM(Quantity)
FROM `new-project-jun21.sales.sales_info`
WHERE EXTRACT(YEAR FROM date) = t.year - 1
AND EXTRACT(MONTH FROM date) = t.month
AND ProductId = t.ProductId
) AS UnitsSold_LY
FROM xrows t
ORDER BY product, year, month
;
Adjusted to remove the unnecessary GROUP BY in the correlated subquery.

T-SQL query to summarize total per month per year, and cumulative amounts to date

I have a database table that captures every Sales Transaction:
Transactions
(
ID INT,
TransactionDate DATETIME,
SalesAmount MONEY
)
I want to write a T-SQL query which returns a report (snapshot sample below). First column it lists the month, next column Total-Sales per month within year, and last column cumulative amount of that year up to this month. Only for year of 2018.
Any thoughts or solutions? Thank you.
Try this:
;with cte as
(
Select
YEAR(TransactionDate) as [Year],
MONTH(TransactionDate) as [Month],
SUM (SalesAmount) as [MonthlySales],
DATEPART(m, TransactionDate) as [MonthNumber]
from Transactions
group by YEAR(TransactionDate), MONTH(TransactionDate)
)
select
a.[Month], a.MonthlySales as [MonthlySales 2018], SUM(b.MonthlySales) as [Cumulative 2018]
from cte a inner join cte b on a.MonthNumber >= b.MonthNumber
WHERE (a.[Year]) = 2018 AND (b.[Year]) = 2018
group by a.[Month], a.MonthlySales
ORDER by a.[Month]
Try this one:
With Q
as
(
Select DatePart(yyyy,TransactionDate) 'Year',DatePart(m,TransactionDate) 'Month', sum(SalesAmount) 'Sales'
From Transactions
Group by DatePart(yyyy,TransactionDate),DatePart(m,TransactionDate)
)
Select q.Year,q.Month,( Select sum(q1.Sales)
From Q q1
Where q1.Year=q.Year
And q1.Month <= q.Month
) 'Cumulative Sale'
From Q q
Order by q.Year,q.Month
You would use aggregation and window functions:
select datename(month, transaction_date) as mon,
sum(salesAmount) as monthly_sales,
sum(salesAumount) over (order by min(transaction_date)) as running_amount
from transactions t
where t.transaction_date >= '2018-01-01' and
t.transaction_date < '2019-01-01'
group by datename(month, transaction_date)
order by min(transaction_date);

Group by month and add year and employee

I have a simple table with every sale made in few past years. I would like to find out maximum sale per month and who made it and in which year and month.
Table has following columns:
Id, date, amount, employeeId
I group data by year(date), month(date), employeeId and use sum(amount) to find sale of each employee in each month. Then I group further by month(date) and use max on sum(amount) column to find maximum sale per month. This is easy.
After that I would like to find out when exactly (date) and who exactly (employeeId) made that particular sale.
Group data by year(date), month(date), employeeId and use sum(amount) to find sale of each employee in each month then order by sum(amount) desc. Your highest sellers will be at the top of the results.
Most databases support the ANSI standard rank() function. This may do what you want:
select s.*
from (select year(date) as yyyy, month(date) as mm, employeeid, sum(amount) as amount,
rank() over (partition by year(date), month(date)
order by sum(amount) desc
) as seqnum
from simpletable
) s
where seqnum = 1;
I think you can do this like, if I understand correctly:
select top 1 with ties year(date), month(date), employeeId
from TableName
group by year(date), month(date), employeeId
order by sum(amount) desc

SQL - monthly average rather than daily average

I have a table called values that contains 3 columns - Location, value, date
I want to work out the average value per month
so far I have
SELECT Location, Avg(value), date
FROM Value
GROUP BY Location, date
This returns the average values but a value is entered on a daily basis so I have an average per day rather than per month, how can I achieve a monthly average?
try this:
SELECT Location,
Avg(value),
month(date),
year(date)
FROM Value
GROUP BY Location,
month(date),
year(date)
You can use the following, if you want month only grouping:
SELECT Location, Avg(value) AvgVal, Month(date) Mnth
FROM Value
GROUP BY Location, Month(date)
You can even use GROUPING SETS, which will GROUP BY Month, year, location and then give you a total for all:
SELECT Location,
Avg(value) AvgVal,
Month(dt) Mnth,
Year(dt) Yr
FROM yourtable
GROUP BY
GROUPING SETS((Month(dt), Year(dt), Location), (Location));
See SQL Fiddle with Demo
SELECT
Location,
year(date),
month(date),
Avg(value)
FROM
Value
GROUP BY
Location,
year(date),
month(date)
Can also do it as the following if you want to combine the dates into one column with the first of the month as the day.
SELECT Location,
Avg(value),
DateFromParts(Year(date), Month(date) , 1) AS FirstOfMonthDate
FROM Value
GROUP BY Location,
DateFromParts(Year(date), Month(date) , 1)