SQL Query for YTD & MTD - sql

I am trying to find YTD and MTD totals for total sales. The total sales is derived by multiplying "Order Quantity" and "Unit price" and subtracting "Discount_Applied".
Here is my query,
select orderdate,datename(month,orderdate) as Mnth,year(orderdate) as YR, sum((unit_price*order_quantity)-discount_applied) as Total_Sales,
sum((unit_price*order_quantity)-discount_applied) over (partition by year(orderdate) order by orderdate) as YTD,
sum((unit_price*order_quantity)-discount_applied) over (partition by year(orderdate),datename(month,orderdate) order by orderdate) as MTD
from sales
group by orderdate,datename(month,orderdate),year(orderdate)
However, when I run this query, it gives me an error saying
Column 'sales.Unit_Price' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
I guess it has something to do with the windows function I am using, but can't figure out specifically the problem. Can someone help.

When you use a window function, it is calculated after any possible GROUP BY aggregation. So those columns don't exist anymore, only the aggregation exists.
Instead, you need to SUM the SUM: you need to do a windowed sum over the aggregate sum.
SELECT
s.orderdate,
DATENAME(month, s.orderdate) as Mnth,
YEAR(s.orderdate) as YR,
SUM((s.unit_price * s.order_quantity) - s.discount_applied) as Total_Sales,
SUM(SUM((s.unit_price * s.order_quantity) - s.discount_applied) OVER
(PARTITION BY YEAR(s.orderdate) ORDER BY EOMONTH(s.orderdate), s.orderdate ROWS UNBOUNDED PRECEDING) as YTD,
SUM(SUM((s.unit_price * s.order_quantity) - s.discount_applied)) OVER
(PARTITION BY YEAR(s.orderdate), EOMONTH(s.orderdate) ORDER BY s.orderdate ROWS UNBOUNDED PRECEDING) as MTD
FROM sales s
GROUP BY
s.orderdate;
Note also that EOMONTH is a little more efficient than DATENAME, and that adding the month to the YTD ordering means it can use the same sort, without affecting the calculation.
Also ROWS UNBOUNDED PRECEDING is a little more efficient than the default RANGE UNBOUNDED PRECEDING.

Related

Postgres - AVG calculation

Please refer to the below query
SELECT sum(sales) AS "Sales",
sum(discount) AS "discount",
year
FROM Sales_tbl
WHERE Group by year
Now I want to also display a column for AVG(sales) that is the same value and based on the total of sales column
Output
Please advise
Use AVG() as a window function:
WITH t AS (
SELECT
SUM(sales) AS sales, SUM(discount) AS discount, year
FROM tbl_sales
GROUP BY year
)
SELECT *,AVG(sales) OVER w_total
FROM t
WINDOW w_total AS (RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
ORDER BY year;
The frame RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING is pretty much optional in this case, but it is considered a good practice to be as explicit as possible in window functions. So you're also able to write the query like this:
WITH t AS (
SELECT
SUM(sales) AS sales, SUM(discount) AS discount, year
FROM tbl_sales
GROUP BY year
)
SELECT *,AVG(sales) OVER ()
FROM t
ORDER BY year;
Demo: db<>fiddle

How to create query for search total sales previous year

I have table named Sales.OrderValues that contain of 2 column, namely orderyear and val (total sales per day).
This is the record snippet (I cant show all of record because there are 830 rows)
I want to show the result like this
But, my output is different with my expected output.
As you can see, the expected output of prevtotalsales in 2008 is 618085.30. But, my output is 825169.29 (which is 208083.99 + 617085.30).
Below is my query
SELECT
YEAR(D1.orderdate) AS orderyear,
SUM(D1.val) AS curtotalsales,
(
SELECT
SUM(D2.val)
FROM
Sales.OrderValues D2
WHERE
YEAR(D1.orderdate) > YEAR(D2.orderdate)
)
AS prevtotalsales
FROM
Sales.OrderValues D1
GROUP BY
YEAR(D1.orderdate);
How to show the SUM of totalsales at the previous year without adding the next year's totalsales?
Basically, you want an equality condition in the WHERE clause of the subquery. This:
WHERE YEAR(D1.orderdate) > YEAR(D2.orderdate)
Should be:
WHERE YEAR(D1.orderdate) = YEAR(D2.orderdate) + 1
But it is much simpler and more efficient to just use lag():
SELECT
YEAR(orderdate) AS orderyear,
SUM(val) AS curtotalsales,
LAG(SUM(val)) OVER(ORDER BY YEAR(orderdate)) AS prevtotalsales
FROM Sales.OrderValues
GROUP BY YEAR(orderdate)
ORDER BY orderyear
You need to first SUM the values per year, and then use a cumulative SUM:
WITH Totals AS(
SELECT YEAR(OV.orderdate) AS OrderYear
SUM(OV.Val) AS YearSum
FROM Sales.OrderValues OV
GROUP BY YEAR(OV.orderdate))
SELECT OrderYear,
YearSum,
SUM(YearSum) OVER (ORDER BY OrderYear ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS PreviousCumulative
FROM Totals;

Finding sales growth from cumulative totals over period in SQL

SELECT CUST_ID,CONTACTS
Sum("CONTACTS") Over (PARTITION by "CUST_ID" Order By "end_Period" ROWS UNBOUNDED PRECEDING) as RunningContacts,
"SALES",
Sum("SALES") Over (PARTITION by "CUST_ID" Order By "end_Period" ROWS UNBOUNDED PRECEDING) as RunningSales,
end_Period
FROM Table2
I have currently created the Running growth column in excel formula is (New Runningsales - Previous Running sales) / Previous RunningSales.
Any help here is appreciated.
Are you looking for this?
select t.*,
RunningSales / (Running - Sales) - 1
from (< your query here > ) x
The SQL derived table can hold a query to aggregate sales by period, and you an join such to itself to compare each period to the prior period.

Use a regular aggregative function (sum) alongside a window function

I was reading this tutorial on how to calculate running totals.
Copying the suggested approach I have a query of the form:
select
date,
sum(sales) over (order by date rows unbounded preceding) as cumulative_sales
from sales_table;
This works fine and does what I want - a running total by date.
However, in addition to the running total, I'd also like to add daily sales:
select
date,
sum(sales),
sum(sales) over (order by date rows unbounded preceding) as cumulative_sales
from sales_table
group by 1;
This throws an error:
SYNTAX_ERROR: line 6:8: '"sum"("sales") OVER (ORDER BY "activity_date" ASC ROWS UNBOUNDED PRECEDING)' must be an aggregate expression or appear in GROUP BY clause
How can I calculate both daily total as well as running total?
I think you can try it, but it will repeat your daily_sales. In this way you don't need to group by your date field.
SELECT date,
SUM(sales) OVER (PARTITION BY DATE) as daily_sales
SUM(sales) OVER (ORDER BY DATE ROWS UNBOUNDED PRECEDING) as cumulative_sales
FROM sales_table;
Presumably, you intend an aggregation query to begin with:
select date, sum(sales) as daily_sales,
sum(sum(sales)) over (order by date rows unbounded preceding) as cumulative_sales
from sales_table
group by date
order by date;

Sum over partition not working

I've got some code with the partition function, but it's not working.
I get an error message that says
Incorrect syntax near 'Sales'
Does anyone know why? I looked at the other partition questions, didn't find an answer,
The code (below) is supposed to select PriceZoneID and Sales from the Aggregated Sales History table then sum up the total sales using the OVER function and put that data in a new column called Total Sales.
It should then sum up the sales for each zone using the OVER (PARTITION) expression in a new column called TotalSalesByZone then order the data by Price Zone ID and Sales
Select PriceZoneID,
Sales,
SUM(Sales) OVER () AS Total Sales,
SUM(Sales) OVER (PARTITION BY PriceZoneID) AS TotalSalesByZone
From AggregatedSalesHistory
ORDER BY PriceZoneID AND Sales;
(Partition By divides the result into Partitions eg Zones)
If you could post the code with the correct answer, it would be greatly appreciated!
Coming out of the comments now, as it's getting a little silly to correct the errors in there. There is 1 typograhical error in your code, and 1 syntax error:
Select PriceZoneID,
Sales,
SUM(Sales) OVER () AS Total Sales, --There's a space in the alias
SUM(Sales) OVER (PARTITION BY PriceZoneID) AS TotalSalesByZone
FROM AggregatedSalesHistory
ORDER BY PriceZoneID AND Sales; --AND is not valid in an ORDER BY clause
The correct query would be:
Select PriceZoneID,
Sales,
SUM(Sales) OVER () AS TotalSales, --Removed Space
SUM(Sales) OVER (PARTITION BY PriceZoneID) AS TotalSalesByZone
FROM AggregatedSalesHistory
ORDER BY PriceZoneID, Sales; --Comma delimited