I need help writing a query that shows the amount of orders for each month of the year and also the cumulative amount each year - sql

I am so close to answering this question, but can't figure out the final bits.
I am using the AdventureWorks2019 database.
My code is:
with cte as
(
Select
year(orderdate) as "Years",
month(orderdate) as "Months",
sum(SubTotal) as "MonthlySales",
datepart(m, orderdate) as "MonthNumber"
from sales.SalesOrderHeader
group by year(orderdate), month(orderdate)
)
select
Years, Months, MonthlySales as "MonthlySales", sum(MonthlySales) over (partition by months order by MonthlySales
rows between unbounded preceding and current row) as "Cumulative"
from cte
group by Years, Months, MonthlySales
ORDER by Years, Months
I cant figure out how to order the 'over' part of the query. I know it needs to be "rows between unbounded preceding and current row", but because MonthlySales is sum(totaldue), adding it to another SUM() probably messes it up.
Honestly this question is pretty tough, and I gave it my best.
Thank you for your help!

You can use a window function directly in the aggregation:
Select year(orderdate) as Years, month(orderdate) as Months,
sum(SubTotal) as MonthlySales,
case when grouping(month(orderdate)) = 0
then sum(case when month(orderdate) is not null then sum(SubTotal) end) over (partition by year(orderdate) order bymonth(orderdate))
end as YTD_MonthlySales
from sales.SalesOrderHeader
group by grouping sets (
(year(orderdate), month(orderdate)),
(year(orderdate))
);

Please check and it'll return desired output.
-- SQL SERVER
SELECT t.OrderYear
, CASE WHEN t.OrderMonth IS NULL THEN 'Grand Total' ELSE CAST(t.OrderMonth AS VARCHAR(20)) END b
, t.MonthlySales
, MAX(t.cum_total) cum_total
FROM (SELECT
YEAR(OrderDate) AS OrderYear,
MONTH(OrderDate) AS OrderMonth,
SUM(SubTotal) AS MonthlySales,
SUM(SUM(SubTotal)) OVER (ORDER BY YEAR(OrderDate), MONTH(OrderDate) ROWS UNBOUNDED PRECEDING) cum_total
FROM Sales.SalesOrderHeader
GROUP BY GROUPING SETS ((YEAR(OrderDate), MONTH(OrderDate)))) t
GROUP BY GROUPING SETS ((t.OrderYear
, t.OrderMonth
, t.MonthlySales), t.OrderYear);
Also Check data from https://dbfiddle.uk/?rdbms=sqlserver_2019&sample=adventureworks&fiddle=e6cd2ba8114bd1d86b8c61b1453cafcf

Related

SQL - get start & end balance for each member each year

so I'd like to effectively get for each year the starting and end balance for each member for every year there is a record. for example the below would give me the latest balance for each member each year based on the date column
SELECT
T.MemberID,
T.DateCol,
T.Amount
FROM
(SELECT T.MemberID,
T.DateCol,
Amount,
ROW_NUMBER() OVER (PARTITION BY MemberID,
YEAR(DateCol)
ORDER BY
DateCol desc) AS seqnum
FROM
Tablet T
GROUP BY DateCol, MemberID, Amount
) T
WHERE
seqnum = 1 AND
MemberID = '1000009'
and the below would give me the earliest balance for each year
SELECT
T.MemberID,
T.DateCol,
T.Amount
FROM
(SELECT T.MemberID,
T.DateCol,
Amount,
ROW_NUMBER() OVER (PARTITION BY MemberID,
YEAR(DateCol)
ORDER BY
DateCol) AS seqnum
FROM
Tablet T
GROUP BY DateCol, MemberID, Amount
) T
WHERE
seqnum = 1 AND
MemberID = '1000009'
This would give me a result set like the below, column titles (MemberID, Date, Amount)
What I'm looking for is one query which is done by YEAR, MEMBERID, STARTBALANCE, ENDBALANCE as the columns. And would look like the below
What would be the best way to go about this?
commented above

How to add a new column that summarize rows

I have two issues :
I used 'Rollup' function to add Totals per Month and Year and I would like to change 'NULL' into grand_total as in the attached screenshot
I dont know how to add a new column that will summarize values starting from the second row
Please see attached screenshot of the results I need to receive and an example for a code from my side with a screenshot of the source output : [1]: https://i.stack.imgur.com/6B70o.png
[1]: https://i.stack.imgur.com/E2x8K.png
Select Year(Modifieddate) AS Year,
MONTH(modifieddate) as Month,
Sum(linetotal) as Sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate),MONTH(modifieddate))
Thanks in advance,
I think this will work:
Select Year(Modifieddate) AS Year,
coalesce(convert(varchar(255), month(modifieddate)), 'Grand Total') as Month,
Sum(linetotal) as Sum_price,
sum(sum(linetotal)) over (partition by Year(Modifieddate)
order by coalesce(month(modifieddate), 100)
) as ytd_sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate), month(modifieddate))
The coalesce() in the order by is to put the summary row last for the cumulative sum.
Like this:
Select Year(Modifieddate) AS Year, MONTH(modifieddate) as Month, Sum(linetotal) as Sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate),MONTH(modifieddate))
UNION
Select Year(Modifieddate) AS Year, 'grand_total' as Month, Sum(linetotal) as Sum_price
from Sales.SalesOrderDetail
Group by Year(Modifieddate)
-- SQL SERVER
SELECT t.OrderYear
, CASE WHEN t.OrderMonth IS NULL THEN 'Grand Total' ELSE CAST(t.OrderMonth AS VARCHAR(20)) END b
, t.MonthlySales
, MAX(t.cum_total) cum_total
FROM (SELECT
YEAR(OrderDate) AS OrderYear,
MONTH(OrderDate) AS OrderMonth,
SUM(SubTotal) AS MonthlySales,
SUM(SUM(SubTotal)) OVER (ORDER BY YEAR(OrderDate), MONTH(OrderDate) ROWS UNBOUNDED PRECEDING) cum_total
FROM Sales.SalesOrderHeader
GROUP BY GROUPING SETS ((YEAR(OrderDate), MONTH(OrderDate)))) t
GROUP BY GROUPING SETS ((t.OrderYear
, t.OrderMonth
, t.MonthlySales), t.OrderYear);
Please check this url https://dbfiddle.uk/?rdbms=sqlserver_2019&sample=adventureworks&fiddle=e6cd2ba8114bd1d86b8c61b1453cafcf
To build one #GordonLinoff's answer, you are really supposed to use the GROUPING() function to check whether you are dealing with the grouping column. This behaves better in the face of nullable columns.
Select case when grouping(Year(Modifieddate)) = 0
then Year(Modifieddate)
else 'Grand Total' end AS Year,
case when grouping(month(modifieddate)) = 0
then convert(varchar(255), month(modifieddate))
else 'Grand Total' end as Month,
Sum(linetotal) as Sum_price,
sum(sum(linetotal)) over (
partition by
grouping(Year(Modifieddate)),
grouping(month(modifieddate)),
Year(Modifieddate)
order by month(modifieddate)
) as ytd_sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate), month(modifieddate));

Finding least and highest month revenue

I was having a problem for organizing a table where shows me
the month with the highest revenue and the lowest order by revenue
all the information is in the same table (Ordertable)
So I have orderdate and orderfinalprice.
select orderdate as MonthsSales, Highestrevenue, Lowestrevenue
from
(select months
, max (orderfinalprice) as Highestrevenue, min (orderfinalprice) as Lowestrevenue
From hologicOrder_T)
order by monthsales;
What's missing is the grouping by month based on orderdate.
select orderdate as MonthsSales, Highestrevenue, Lowestrevenue
from
(select to_char(orderdate, 'Month') orderdate, max(orderfinalprice) as Highestrevenue
, min (orderfinalprice) as Lowestrevenue
from hologicOrder_T
group by to_char(orderdate, 'Month'))
order by monthsales;
As far as I understood your problem, you want two months data, one with highest revenue and one with lowest revenue.
You can get it by following:
Select max(case when minrn = 1 then month_ end) lowestrevenue_month,
max(case when minrn = 1 then totalrevenue end) lowestrevenue,
max(case when maxrn = 1 then month_ end) highestrevenue_month,
max(case when maxrn = 1 then totalrevenue end) highestrevenue
From
(Select trunc(orderdate, 'month') month_,
Sum(orderfinalprice) as totalrevenue,
Row_nuumber() over (partition by trunc(orderdate, 'month') order by Sum(orderfinalprice)) as minrn,
Row_nuumber() over (partition by trunc(orderdate, 'month') order by Sum(orderfinalprice) desc) as maxrn
From hologicOrder_T
Group by trunc(orderdate, 'month') )
Where 1 in (minrn, maxrn);
You should not use to_char(orderdate, 'Month') because it will be same for the month regardless of its year.
In output, highestrevenue_month and lowestrevenue_month will be first date of the month, you can format it accordingly using to_char.
Cheers!!

Running Totals for the year

Trying to create running totals based on the year in my query as i'm showing the last 3 years of sales and commissions in my query and want running yearly totals for those for each salesperson listed
Tried various ways to get the data to do this but haven't been able to.
SELECT TOP (100) PERCENT 'abc' AS CompanyCode, abc.AR_Salesperson.SalespersonName, abc.AR_SalespersonCommission.SalespersonDivisionNo, abc.AR_SalespersonCommission.SalespersonNo,
SUM(abc.AR_SalespersonCommission.InvoiceTotal) AS InvoiceTotalSum, SUM(abc.AR_SalespersonCommission.CommissionAmt) AS CommissionAmtSum, DATENAME(month, abc.AR_SalespersonCommission.InvoiceDate)
AS Month, DATENAME(year, abc.AR_SalespersonCommission.InvoiceDate) AS Year, DATEPART(m, abc.AR_SalespersonCommission.InvoiceDate) AS MonthNumber
FROM abc.AR_Customer INNER JOIN
abc.AR_SalespersonCommission ON abc.AR_Customer.ARDivisionNo = abc.AR_SalespersonCommission.ARDivisionNo AND abc.AR_Customer.CustomerNo = abc.AR_SalespersonCommission.CustomerNo INNER JOIN
abc.AR_Salesperson ON abc.AR_SalespersonCommission.SalespersonDivisionNo = abc.AR_Salesperson.SalespersonDivisionNo AND
abc.AR_SalespersonCommission.SalespersonNo = abc.AR_Salesperson.SalespersonNo
GROUP BY abc.AR_Salesperson.SalespersonName, abc.AR_SalespersonCommission.SalespersonDivisionNo, abc.AR_SalespersonCommission.SalespersonNo, DATENAME(month, abc.AR_SalespersonCommission.InvoiceDate),
DATENAME(year, abc.AR_SalespersonCommission.InvoiceDate), DATEPART(m, abc.AR_SalespersonCommission.InvoiceDate)
HAVING (DATENAME(year, abc.AR_SalespersonCommission.InvoiceDate) > DATEADD(year, - 3, GETDATE()))
UNION
SELECT TOP (100) PERCENT 'XYZ' AS CompanyCode, xyz.AR_Salesperson.SalespersonName, xyz.AR_SalespersonCommission.SalespersonDivisionNo, xyz.AR_SalespersonCommission.SalespersonNo,
SUM(xyz.AR_SalespersonCommission.InvoiceTotal) AS InvoiceTotalSum, SUM(xyz.AR_SalespersonCommission.CommissionAmt) AS CommissionAmtSum, DATENAME(month, xyz.AR_SalespersonCommission.InvoiceDate)
AS Month, DATENAME(year, xyz.AR_SalespersonCommission.InvoiceDate) AS Year, DATEPART(m, xyz.AR_SalespersonCommission.InvoiceDate) AS MonthNumber
FROM xyz.AR_Customer INNER JOIN
xyz.AR_SalespersonCommission ON xyz.AR_Customer.ARDivisionNo = xyz.AR_SalespersonCommission.ARDivisionNo AND xyz.AR_Customer.CustomerNo = xyz.AR_SalespersonCommission.CustomerNo INNER JOIN
xyz.AR_Salesperson ON xyz.AR_SalespersonCommission.SalespersonDivisionNo = xyz.AR_Salesperson.SalespersonDivisionNo AND
xyz.AR_SalespersonCommission.SalespersonNo = xyz.AR_Salesperson.SalespersonNo
GROUP BY xyz.AR_Salesperson.SalespersonName, xyz.AR_SalespersonCommission.SalespersonDivisionNo, xyz.AR_SalespersonCommission.SalespersonNo, DATENAME(month, xyz.AR_SalespersonCommission.InvoiceDate),
DATENAME(year, xyz.AR_SalespersonCommission.InvoiceDate), DATEPART(m, xyz.AR_SalespersonCommission.InvoiceDate)
HAVING (DATENAME(year, xyz.AR_SalespersonCommission.InvoiceDate) > DATEADD(year, - 3, GETDATE()))
I expect the output to have running totals for the InvoiceTotalSum and CommissionAmt for each salesperson for the last 3 years. So of course January will be 0 for each person but Feb through December will have a running total
Sample data and desired results below. Desired results are the highlighted columns
Sample Data and Desired Results
2 things before I go to the solution.
First, I am not sure why you need an UNION into your query. I can see the difference between abc and xyz but it still looks strange.
It is surely possible your query can be shortened/simplified, which would need more info to tell.
Second, I do not see a valid reason why the running total should be 0 for January.
Explanation about that:
February (2nd month of the year): running total in your expected result contains the amount for 2 months
March: 3 months
April: 4 months
...
So January should contain the running total for 1 month (January itself).
Try the query below:
WITH MyData AS (
<Please paste your query here>
)
SELECT CompanyCode, SalesPersonName, SalesPersonDivisionNo, InvoiceTotalSum,
SUM(InvoiceTotalSum) OVER (PARTITION BY SalesPersonDivisionNo, SalesPersonNo, SalesPersonName, Year ORDER BY MonthNumber) AS InvoiceTotalRunningSum,
CommissionAmtSum,
SUM(CommissionAmtSum) OVER (PARTITION BY SalesPersonDivisionNo, SalesPersonNo, SalesPersonName, Year ORDER BY MonthNumber) AS CommissionAmtRunningSum,
Month, Year, MonthNumber
FROM MyData
ORDER BY CompanyCode, SalesPersonDivisionNo, SalesPersonNo, SalesPersonName, Year, MonthNumber
The magic takes place in the PARTION BY/ORDER BY
I think you need to review your query and simplify it.
a few notes :
if the CompanyCode is already existed within the database, join its table and link it with the current records instead of writing it manually.
DATENAME(year, ... ) the shorthand is YEAR()
DATEPART(m, ...) the shorthand is MONTH()
I encourage you to use aliases
(DATENAME(year, xyz.AR_SalespersonCommission.InvoiceDate) > DATEADD(year, - 3, GETDATE())) will exclude the first year and include the current.So, 2019-3 = 2016, yours will get 2017,2018, and 2019, while it should get 2016,2017, and 2018.
for your InvoiceTotalRunningSum use :
SUM(InvoiceTotalSum) OVER (PARTITION BY SalespersonNo ORDER BY MonthNumber UNBOUNDED PRECEDING)
this will do an accumulative sum on InvoiceTotalSum for each SalespersonNo. you can partition the records for each year, month ..etc. simply by adding more partitions, but I used your current query as sub-query, and did that instead :
read more about SELECT - OVER Clause (Transact-SQL)
try it out :
SELECT
'abc' AS CompanyCode
, SalespersonName
, SalespersonDivisionNo
, SalespersonNo
, InvoiceTotalSum
, SUM(InvoiceTotalSum) OVER (PARTITION BY SalespersonNo ORDER BY MonthNumber UNBOUNDED PRECEDING) InvoiceTotalRunningSum
, CommissionAmtSum
, SUM(CommissionAmtSum) OVER (PARTITION BY SalespersonNo ORDER BY MonthNumber UNBOUNDED PRECEDING) CommissionAmtRunningSum
, [Month]
, [Year]
, MonthNumber
FROM (
SELECT
'abc' AS CompanyCode
, ars.SalespersonName
, arsc.SalespersonDivisionNo
, arsc.SalespersonNo
, SUM(arsc.InvoiceTotal) InvoiceTotalSum
, SUM(arsc.CommissionAmt) CommissionAmtSum
, DATENAME(month, arsc.InvoiceDate) [Month]
, YEAR(arsc.InvoiceDate) [Year]
, MONTH(arsc.InvoiceDate) MonthNumber
FROM
abc.AR_Customer arc
INNER JOIN abc.AR_SalespersonCommission arsc ON arc.ARDivisionNo = arsc.ARDivisionNo AND arc.CustomerNo = arsc.CustomerNo
INNER JOIN abc.AR_Salesperson ars ON arsc.SalespersonDivisionNo = ars.SalespersonDivisionNo AND arsc.SalespersonNo = ars.SalespersonNo
GROUP BY
ars.SalespersonName
, arsc.SalespersonDivisionNo
, arsc.SalespersonNo
, YEAR(arsc.InvoiceDate)
, MONTH(arsc.InvoiceDate)
, DATENAME(month, arsc.InvoiceDate)
HAVING
YEAR(arsc.InvoiceDate) BETWEEN YEAR(GETDATE()) - 3 AND YEAR(GETDATE()) - 1 -- Only include the last three years (excluding current year)
) D

SQL YTD and last year YTD on complete data

I need to calculate YTD and last year YTD on a table [SQL Server 2012]. Below is the query I tried. Its getting doubled and tripled for some cases.
SELECT SUM(A.RevisionNumber)YTD,SUM(P.RevisionNumber)LY_YTD,B.OrderDateM,B.OrderDateY
FROM
(select MONTH(OrderDate)OrderDateM,YEAR(OrderDate)OrderDateY from sales.SalesOrderHeader B
group by MONTH(OrderDate),YEAR(OrderDate))B
LEFT JOIN
(select SUM(RevisionNumber)RevisionNumber,MONTH(OrderDate)OrderDateM,YEAR(OrderDate)OrderDateY
from sales.SalesOrderHeader
group by MONTH(OrderDate),YEAR(OrderDate))A
ON A.OrderDateM<=B.OrderDateM AND A.OrderDateY=B.OrderDateY
LEFT JOIN
(select SUM(RevisionNumber)RevisionNumber,MONTH(OrderDate)OrderDateM,YEAR(OrderDate)OrderDateY
from sales.SalesOrderHeader
group by MONTH(OrderDate),YEAR(OrderDate))P
ON P.OrderDateM<=B.OrderDateM AND P.OrderDateY=B.OrderDateY-1
GROUP BY B.OrderDateM,B.OrderDateY
ORDER BY B.OrderDateY,B.OrderDateM
You can use windowing function as below:
;With cte as (
Select Sum(RevisionNumber) As SM_RevisionNumber, Month(OrderDate) as OrderM,
Year(OrderDate) as OrderY
From Sales.SalesOrderHeader
Group by Month(OrderDate), Year(OrderDate)
), cte2 as (
Select YTD = Sum(SM_RevisionNumber) over (partition by OrderY order by OrderM),
OrderM, OrderY, RowN = Row_Number() over(order by OrderY, OrderM)
from cte
)
Select YTD, LY_YTD = lag(YTD, 12, null) over(Order by RowN), OrderM, ORderY
from cte2
But this solution assumes we have atleast one entry for each month and year.