Pivot table & Group by - sql

I'm using this query to get previous 12 months Sales.
SELECT *
FROM (SELECT
gp_etablissement [ETABLISSEMENT],
datename(month, dateadd(m,-1,getdate())) [Month],
COUNT(1) [Sales Count]
FROM PIECE
GROUP BY gp_etablissement,
datename(month, dateadd(m,-1,getdate()))) AS MontlySalesData
PIVOT( SUM([Sales Count])
FOR Month IN ([January],[February],[March],[April],[May],
[June],[July],[August],[September],[October],[November],
[December])) AS MNamePivot
I got this error :
Each GROUP BY expression must contain at least one column that is not
an outer reference.
I've changed many columns in the group by, but same problem.

Use the below code for eliminating the error. The pivot doesn't work with the subquery that way when the group by uses date parts.
;WITH CTE
AS
(
SELECT
gp_etablissement [ETABLISSEMENT],
datename(month, dateadd(m,-1,getdate())) [Month],
COUNT(1) [Sales Count]
FROM PIECE
GROUP BY gp_etablissement,
datename(month, dateadd(m,-1,getdate()))
)
SELECT *
FROM CTE
PIVOT( SUM([Sales Count])
FOR Month IN ([January],[February],[March],[April],[May],
[June],[July],[August],[September],[October],[November],
[December])) AS MNamePivot

Related

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));

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

How can I join 2 table together?

I'm trying to create a join table with 2 existing tables. something like below:
This is the first table queries and looks like this
https://ibb.co/sg2MXKf
SELECT
DATEPART( week, dbo.Income.IncomeDate ) AS [Week Income],
DATEPART( YEAR, dbo.Income.IncomeDate ) AS [Year],
SUM ( dbo.Income.CardAmount ) AS [Total Card],
SUM ( dbo.Income.CashAmount ) AS [Total Cash],
SUM ( dbo.Income.TipsAmount ) AS [Total Tip],
SUM ( dbo.Income.SalaryAmount ) AS [Total Salary],
SUM ( dbo.Income.Adjustment ) AS [Total Adjustment]
FROM
dbo.Income
GROUP BY
DATEPART( week, dbo.Income.IncomeDate ),
DATEPART( YEAR, dbo.Income.IncomeDate )
ORDER BY
DATEPART(YEAR, dbo.Income.IncomeDate )
And this is the second table queries and looks like this
https://ibb.co/z8sRwpT
SELECT
DATEPART( wk, dbo.Transactions.PaymentMadeOn ) AS [Week],
COUNT (DATEPART( wk, dbo.Transactions.PaymentMadeOn )) AS [Expenses Count],
DATEPART( YEAR, dbo.Transactions.PaymentMadeOn ) AS [Year],
SUM ( dbo.Transactions.PaymentAmount ) AS [Total]
FROM
dbo.Transactions
GROUP BY
DATEPART( wk, dbo.Transactions.PaymentMadeOn ),
DATEPART( YEAR, dbo.Transactions.PaymentMadeOn )
ORDER BY
DATEPART( YEAR, dbo.Transactions.PaymentMadeOn )
What I expected is something this. Both table 1 and 2 combined, and the total expenses added.
https://ibb.co/0DbZLYV
As commented by SO folks, we cannot access the images that describe your expected results. However we understand that you are looking to JOIN the results of both queries that you are showing.
Here is how to do it : you turn each query into a subquery, and you JOIN them together on their common key fields ; in your use case, this must be Year and Week. The ORDER BY clause needs to be moved to the outer query. In the SELECT, WHERE and ORDER BY clauses, you can freely access all the fields from the subqueries, using the aliases that you defined (here A and B).
Sample code (you will have to adapt the SELECT, I cannot tell what you want it to look like) :
SELECT
A.[Week Income],
A.[Year],
A.[Total] - B.[Total Salary] AS [Balance]
...
FROM (
SELECT
DATEPART( week, dbo.Income.IncomeDate ) AS [Week Income],
DATEPART( YEAR, dbo.Income.IncomeDate ) AS [Year],
SUM ( dbo.Income.CardAmount ) AS [Total Card],
SUM ( dbo.Income.CashAmount ) AS [Total Cash],
SUM ( dbo.Income.TipsAmount ) AS [Total Tip],
SUM ( dbo.Income.SalaryAmount ) AS [Total Salary],
SUM ( dbo.Income.Adjustment ) AS [Total Adjustment]
FROM
dbo.Income
GROUP BY
DATEPART( week, dbo.Income.IncomeDate ),
DATEPART( YEAR, dbo.Income.IncomeDate )
) AS A
LEFT JOIN (
SELECT
DATEPART( wk, dbo.Transactions.PaymentMadeOn ) AS [Week],
COUNT (DATEPART( wk, dbo.Transactions.PaymentMadeOn )) AS [Expenses Count],
DATEPART( YEAR, dbo.Transactions.PaymentMadeOn ) AS [Year],
SUM ( dbo.Transactions.PaymentAmount ) AS [Total]
FROM
dbo.Transactions
GROUP BY
DATEPART( wk, dbo.Transactions.PaymentMadeOn ),
DATEPART( YEAR, dbo.Transactions.PaymentMadeOn )
) AS B ON A.[Week Income] = B.[Week] AND A.[Year] = B.[Year]
ORDER BY
A.[Year],
A.[Week Income]

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.

PIVOT table returning all null data

Consider this query:
SELECT *
FROM (
SELECT
userid,
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
which produces output like this:
And this pivot query in which I am trying to sum over each month:
SELECT
userid,
COALESCE([2016-08-01], 0) AS [2016-08-01],
COALESCE([2016-09-01], 0) AS [2016-09-01]
FROM (
SELECT
userid,
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
PIVOT
(
SUM(amt)
FOR purchasemonth IN ([2016-08-01], [2016-09-01])
) AS PivotTable
which produces output like this:
There is no NULL data at all in the original query's output. The PIVOT query's output is nothing but null data (coalesced to 0). But I can't figure out why the PIVOT is not summing the data as I expected. I'm expecting there to be no NULL data in the PIVOT output either.
How can I fix the query to behave as expected?
PurchaseMonth in your Derived Table is a String without DAYs in it and you are comparing it to a Date with days in it for the values/column names:
So the main issue is this line:
FOR purchasemonth IN ([2016-08-01], [2016-09-01])
TO
FOR purchasemonth IN ([2016-08], [2016-09])
Once you change that you would need to change the COALESCE() statements too
and you should get what you want.
SELECT
userid,
COALESCE([2016-08], 0) AS [2016-08-01],
COALESCE([2016-09], 0) AS [2016-09-01]
FROM (
SELECT
userid,
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
PIVOT
(
SUM(amt)
FOR purchasemonth IN ([2016-08], [2016-09])
) AS PivotTable
If you want the 01 to remain for days then simply change up the Derived Table definition to be a date or include the day in the format
So if you want to go this route change this line:
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
To
DATEADD(day,1-DAY(datecreated),datecreated) AS purchasemonth,
you could also use this
FORMAT(datecreated, 'yyyy-MM-dd') AS purchasemonth,
But FORMAT has performance impacts that you have no reason to introduce if you don't need to.
SELECT
userid,
COALESCE([2016-08-01], 0) AS [2016-08-01],
COALESCE([2016-09-01], 0) AS [2016-09-01]
FROM (
SELECT
userid,
DATEADD(day,1-DAY(datecreated),datecreated) AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
PIVOT
(
SUM(amt)
FOR purchasemonth IN ([2016-08-01], [2016-09-01])
) AS PivotTable