Running Totals for the year - sql

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

Related

SQL Get Quarter over Quarter values

I'd Like to get QoQ from a dataset with Q3 and Q4 data that also has a report date column, each row should have a QoQ value for each fiscal month (represented by a report date), Q4 should compare against Q3 but my statement only seems to be comparing within the same quarter i.e. Q4 is comparing against Q4 instead of Q4 comparing to Q3 ..
I am using the lag function but not sure what I am doing wrong if someone could please see code below.
SELECT [Year],
[SalesDate] as Report_Date,
[Quarter],
Sales,
LAG(Sales, 1, 0) OVER(
PARTITION BY [Year] ,[Quarter]
ORDER BY [Year],
[Quarter],
salesDate
ASC) AS [QuarterSales_Offset],
sales - LAG(Sales) OVER(
PARTITION BY [Year] ,[Quarter]
ORDER BY [Year],
[Quarter],
salesDate
ASC) as diff,
Case When
LAG(Sales,1,0) OVER(
PARTITION BY [Year],[Quarter]
ORDER BY [Year],
[Quarter],
salesDate
ASC) = 0 then null else
(
sales - LAG(Sales,1,0) OVER(
PARTITION BY [Year],[Quarter]
ORDER BY [Year],
[Quarter],
salesDate
ASC))/ LAG(Sales,1,0) OVER(
PARTITION BY [Year],[Quarter]
ORDER BY [Year],
[Quarter],
salesDate
ASC) end as QoQ
FROM dbo.ProductSales_2;
Query Output:
Since LAG() at 1 offset returns previous row and your data is at month level, you actually compare month over month in each quarter. Consider a different approach such as joining two subsets of your data by quarter and month in quarter.
QuarterMonth column can be calculated with ROW_NUMBER() expression (i.e., running count of months within each quarter). Since month gaps in sales data can potentially arise, use a year_quarter_month calendar table aligned to your fiscal year. Altogether, this allows comparison of first FY Q4 month (2020-08-31) to first FY Q3 month (2020-05-31) by columns.
WITH unit AS (
SELECT yqm.[Year]
, yqm.[Quarter]
, yqm.[Month]
, COALESCE(p.[Report_Date], DATEADD(DAY, -1, DATEFROMPARTS(yqm.[Year], yqm.[Month]+1, 1))) AS [Report_Date]
, p.[Sales]
FROM year_quarter_month_table yqm
LEFT JOIN dbo.ProductSales_2 p
ON yq.[Year] = p.[Year]
AND yq.[Quarter] = p.[Quarter]
AND yq.[Month] = p.[Month]
), sub AS (
SELECT [Year]
, [Quarter]
, [Month]
, ROW_NUMBER() OVER(PARTITION BY [Year], [Quarter]
ORDER BY [Report_Date]) AS [QuarterMonth]
, [Report_Date]
, [Sales]
FROM unit
)
SELECT q4.[Year]
, q4.[Report_Date] AS Q4_Date
, q4.[Sales] AS Q4_Sales
, q3.[Report_Date] AS Q3_Date
, q3.[Sales] AS Q3_Sales
, q4.[Sales] - q3.[Sales] AS Diff
, COALESCE((q4.[Sales] - q3.[Sales]) / q3.[Sales], 0) AS QoQ
FROM sub q4
LEFT JOIN sub q3
ON q4.[Year] = q3.[Year]
AND q4.[Quarter] = 4
AND q3.[Quarter] = 3
AND q4.[QuarterMonth] = q3.[QuarterMonth]
You may be able to generalize to any quarter-over-quarter calculation and not just Q3 and Q4:
WITH sub AS (
-- SAME CTEs AS ABOVE
)
SELECT curr_qtr.[Year]
, curr_qtr.[Report_Date] AS Curr_Qtr_Date
, curr_qtr.[Sales] AS Curr_Qtr_Sales
, last_qtr.[Report_Date] AS Last_Qtr_Date
, last_qtr.[Sales] AS Last_Qtr_Sales
, curr_qtr.[Sales] - last_qtr.[Sales] AS Diff
, COALESCE((curr_qtr.[Sales] - last_qtr.[Sales]) / last_qtr.[Sales], 0) AS QoQ
FROM sub curr_qtr
LEFT JOIN sub last_qtr
ON curr_qtr.[Year] = last_qtr.[Year] -- ASSUMING FISCAL YEAR AND NOT CALENDAR YEAR
AND curr_qtr.[Quarter] = last_qtr.[Quarter] + 1
AND curr_qtr.[QuarterMonth] = last_qtr.[QuarterMonth]

Percentage difference from previous year

Trying to add a percent difference column to my data as i'm showing the last 3 years of sales and commissions in my query and want percent of difference for each salesperson listed to show the difference in the amounts that were made for sales and also for commission from the previous year in that same month. So for the amount made in January 2017 for commission and sales, I want to show whether they had an increase or decrease in the amount between what was earned in January 2018 compared to what was earned in January 2017.
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, - 4, GETDATE()))
ORDER BY
SalespersonName,
Year,
MonthNumber
Tried various ways to get the data to do this but haven't been able to, like using OVER PARTITION BY and all that. Desired Results and Sample Data are in the link below.
You can use a CTE and JOIN to it using the previous year:
; WITH L4Y
AS (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, - 4, GETDATE()))
)
SELECT L4Y.*
, InvoiceTotalPrevYear = COALESCE(LY.InvoiceTotalSum, 0)
, CommissionAmtSumPrevYear = COALESCE(LY.CommissionAmtSum, 0)
FROM L4Y
LEFT JOIN L4Y LY
ON L4Y.CompanyCode = LY.CompanyCode
AND L4Y.SalespersonName = LY.SalespersonName
--...(join on ALL fields except YEAR)
AND l4y.[Month] = LY.[Month]
-- Here's where the magic happens:
AND L4Y.[Year] = (LY.[Year]-1) ;

Pivot and INNER JOINs

I have been trying to run a pivot query but I am failing hard, I am very new with all this so please be patient
what I want is to return the Quantities values of each month, jan, feb... dec, for each PartRef
this is what I have
SELECT PartRef
, Year
, fMonth
, sum(Quantity) as Quantity
FROM(SELECT PartRef
, year(DateClosed) as Year
, month(DateClosed) as Month
, SUM(fldShipped) as Quantity
FROM PartsInvoice
INNER JOIN Requests ON PartsInvoice.fID = Requests.WorkItemRef
INNER JOIN PartsLine ON Requests.ID = PartsLine.RequestRef
WHERE Closed = 1 and DateClosed > DateAdd(mm, DateDiff(mm, 0, GetDate()) -12, 0)
GROUP BY PartRef, year(DateClosed), month(DateClosed)
) as SalesHits
PIVOT (
SUM(NOT SURE)FOR NOT SURE IN ([Jan],[Feb],[Mar],[Apr],[May],[June],[July],[Ago],[Sep],[Oct],[Nov],[Dec])
)AS Hits
GROUP BY PartRef, Year, Month
Here you have an example how it works with a table like yours.
declare #table table(
partref int,
year int,
month nvarchar(50),
quantity int
)
insert into #table values
(1,2016,'jan',12),
(1,2016,'feb',12),
(2,2016,'jan',12),
(2,2016,'feb',12),
(1,2016,'jan',12)
select PartRef
, year
, sum([jan]) 'Jan',sum([feb]) 'Feb'
,sum([mar]),sum([apr]),sum([may]),sum([jun]),sum([jul])
,sum([aug]),sum([sep]),sum([oct]),sum([nov]),sum([dec])
from(
SELECT PartRef
, year
, [jan],[feb],[mar],[apr],[may],[jun],[jul],[aug],[sep],[oct],[nov],[dec]
from #table
PIVOT (
SUM(quantity)FOR month IN ([jan],[feb],[mar],[apr],[may],[jun],[jul],[aug],[sep],[oct],[nov],[dec])
)AS Hits
) as t
group by PartRef,year
here is the full scope of my query and I believe I got it to work :)
SELECT *
FROM(SELECT PartRef
, year(DateClosed) as Year
, month(DateClosed) as Month
, SUM(Shipped) as Quantity
FROM PartsInvoice
INNER JOIN Requests ON PartsInvoice.ID = Requests.WorkItemRef
INNER JOIN PartsLine ON Requests.ID = PartsLine.RequestRef
WHERE HasClosed = 1 and DateClosed > DateAdd(mm, DateDiff(mm, 0, GetDate()) -13, 0)
GROUP BY PartRef, year(DateClosed), month(DateClosed)
UNION ALL
--RO
SELECT PartRef
, year(DateClosed) as Year
, month(DateClosed) as Month
, SUM(Shipped) as Quantity
FROM RepairOrder
INNER JOIN Requests ON RepairOrder.ID = Requests.WorkItemRef
INNER JOIN PartsLine ON Requests.ID = PartsLine.RequestRef
WHERE Status = 3 and DateClosed > DateAdd(mm, DateDiff(mm, 0, GetDate()) -13, 0)
GROUP BY PartRef, year(DateClosed), month(DateClosed)
UNION ALL
-- Historical Hits
SELECT PartRef
, year(date) as Year
, month(Date) as Month
, SUM(Quantity) as Quantity
FROM PartsHistoricalHits
WHERE Date > DateAdd(mm, DateDiff(mm, 0, GetDate()) -13, 0)
GROUP BY PartRef, year(Date), month(Date)
) as SalesHits
PIVOT (
COUNT (Quantity)FOR fldMonth IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13])
)AS Hits

How to merge sql query with union into one query

I would like to get the results of the below query with just one query not by using union.
My query is as below
I am generating a SSRS chart this query, so need to merge the query into one and get a proper result as shown in table 2
select
res.Count, res.Month, res.status, res.SortOrder
from
(SELECT
count(analysis_complete_date) as Count,
DATENAME(month, analysis_complete_date) AS Month,
DATEPART(month, analysis_complete_date) AS SortOrder,
'Analysis' as status
FROM
SCN_Part_Details AS parts
WHERE
analysis_complete_date BETWEEN '2014-01-01' AND '2014-12-11'
GROUP BY
DATENAME(month, analysis_complete_date),
DATEPART(month, analysis_complete_date)
union
SELECT
count(Act_Supp_Negotiation_Date) as Count,
DATENAME(month, Act_Supp_Negotiation_Date) AS Month,
DATEPART(month, Act_Supp_Negotiation_Date) AS SortOrder,
'Negotiated' as status
FROM
SCN_Part_Details AS parts
WHERE
Act_Supp_Negotiation_Date BETWEEN '2014-01-01' AND '2014-12-11'
GROUP BY
DATENAME(month, Act_Supp_Negotiation_Date),
DATEPART(month, Act_Supp_Negotiation_Date) ) as res
order by
res.SortOrder
This will give a result like:
Table 1
Count Month Status SortOrder
--------------------------------------------------
167 January Analysis 1
631 January Negotiated 1
70 February Analysis 2
237 February Negotiated 2
and so on..
I want a result like this:
Table 2
AnalysisCount NegotiatedCount Month SortOrder
---------------------------------------------------------
167 631 January 1
70 237 February 2
Give it a try:
;WITH CTEResult AS
(
select
res.Count, res.Month, res.status, res.SortOrder
from
(SELECT
count(analysis_complete_date) as Count,
DATENAME(month, analysis_complete_date) AS Month,
DATEPART(month, analysis_complete_date) AS SortOrder,
'Analysis' as status
FROM
SCN_Part_Details AS parts
WHERE
analysis_complete_date BETWEEN '2014-01-01' AND '2014-12-11'
GROUP BY
DATENAME(month, analysis_complete_date),
DATEPART(month, analysis_complete_date)
union
SELECT
count(Act_Supp_Negotiation_Date) as Count,
DATENAME(month, Act_Supp_Negotiation_Date) AS Month,
DATEPART(month, Act_Supp_Negotiation_Date) AS SortOrder,
'Negotiated' as status
FROM
SCN_Part_Details AS parts
WHERE
Act_Supp_Negotiation_Date BETWEEN '2014-01-01' AND '2014-12-11'
GROUP BY
DATENAME(month, Act_Supp_Negotiation_Date),
DATEPART(month, Act_Supp_Negotiation_Date) ) as res
)
SELECT DISTINCT
(SELECT TOP 1 Count FROM CTEResult A WHERE A.Month = C.Month AND A.STATUS = 'Analysis' AND A.SortOrder = C.SortOrder) AS AnalysisCount,
(SELECT TOP 1 Count FROM CTEResult B WHERE B.Month = C.Month AND B.STATUS = 'Negotiated' AND B.SortOrder = C.SortOrder) AS
NegotiatedCount, C.Month, C.SortOrder
FROM CTEResult C
Once you got your result, you can pivot the table to get the desired result.
create table #temp
(
quantity int,
month varchar(20),
status varchar(20),
sortorder int
)
insert into #temp values
(167,'January' ,'Analysis', 1 ),
(631,'January' ,'Negotiated',1 ),
(70 ,'February','Analysis', 2 ),
(237,'February','Negotiated', 2 )
select max(analysis) as analysiscount
,max(negotiated) as negotiatedcount
,month
,max(sortorder) as sortorder
from
(
select *
from #temp
pivot
(max(quantity) for status in ([analysis],[negotiated])) as pi
) t
group by month
order by sortorder;

Normalization of Year bringing nulls back

I have the following query:
SELECT DISTINCT
YEAR(DateRegistered) as Years,
Months.[MonthName],
COUNT(UserID)as totalReg
FROM
Months WITH(NOLOCK)
LEFT OUTER JOIN
UserProfile WITH(NOLOCK)
ON
Months.MonthID = MONTH(DateRegistered)
AND
DateRegistered > DATEADD(MONTH, -12,GETDATE())
GROUP BY YEAR(DateRegistered), Months.[MonthName]
ORDER BY Months.[MonthName]
As you can tell this will always bring back 12 months worth of data. As such it is working, although there is a bug with this method.
It creates Null values in months where there is no data, now the record should exist(whole point of the query) but Year field is bringing Nulls which is something I dont want.
Now I understand the problem is because there is no data, how is it supposed to know what year?
So my question is - is there any way to sort this out and replace the nulls? I suspect I will have to completely change my methodology.
**YEAR** **MONTH** **TOTAL**
2013 April 1
2013 August 1
NULL December 0
2013 February 8
2013 January 1
2013 July 1
NULL June 0
2013 March 4
NULL May 0
NULL November 0
NULL October 0
2012 September 3
If you want 12 months of data, then construct a list of numbers from 1 to 12 and use these as offsets with getdate():
with nums as (
select 12 as level union all
select level - 1
from nums
where level > 1
)
select YEAR(thedate) as Years,
Months.[MonthName],
COUNT(UserID) as totalReg
FROM (select DATEADD(MONTH, - nums.level, GETDATE()) as thedate
from nums
) mon12 left outer join
Months WITH (NOLOCK)
on month(mon12.thedate) = months.monthid left outer join
UserProfile WITH (NOLOCK)
ON Months.MonthID = MONTH(DateRegistered) and
DateRegistered > DATEADD(MONTH, -12, GETDATE())
GROUP BY YEAR(thedate), Months.[MonthName]
ORDER BY Months.[MonthName];
I find something strange about the query though. You are defining the span from the current date. However, you seem to be splitting the months themselves on calendar boundaries. I also find the table months to be awkward. Why aren't you just using the datename() and month() functions?
Try this out:
;With dates as (
Select DateName(Month, getdate()) as [Month],
DatePart(Year, getdate()) as [Year],
1 as Iteration
Union All
Select DateName(Month,DATEADD(MONTH, -Iteration, getdate())),
DatePart(Year,DATEADD(MONTH, -Iteration, getdate())),
Iteration + 1
from dates
where Iteration < 12
)
SELECT DISTINCT
d.Year,
d.Month as [MonthName],
COUNT(up.UserID)as totalReg
FROM dates d
LEFT OUTER JOIN UserProfile up ON d.Month = DateName(DateRegistered)
And d.Year = DatePart(Year, DateRegistered)
GROUP BY d.Year, d.Month
ORDER BY d.Year, d.Month
Here's my attempt at a solution:
declare #UserProfile table
(
id bigint not null identity(1,1) primary key clustered
, name nvarchar(32) not null
, dateRegistered datetime not null default(getutcdate())
)
insert #UserProfile
select 'person 1', '2011-01-23'
union select 'person 2', '2013-01-01'
union select 'person 3', '2013-05-27'
declare #yearMin int, #yearMax int
select #yearMin = year(MIN(dateRegistered))
, #yearMax= year(MAX(dateRegistered))
from #UserProfile
;with monthCte as
(
select 1 monthNo, DATENAME(month, '1900-01-01') Name
union all
select monthNo + 1, DATENAME(month, dateadd(month,monthNo,'1900-01-01'))
from monthCte
where monthNo < 12
)
, yearCte as
(
select #yearMin yearNo
union all
select yearNo + 1
from yearCte
where yearNo < #yearMax
)
select y.yearNo, m.Name, COUNT(up.id) UsersRegisteredThisPeriod
from yearCte y
cross join monthCte m
left outer join #UserProfile up
on year(up.dateRegistered) = y.yearNo
and month(up.dateRegistered) = m.monthNo
group by y.yearNo, m.monthNo, m.Name
order by y.yearNo, m.monthNo
SQL Fiddle Version: http://sqlfiddle.com/#!6/d41d8/6640
You have to calculate the counts in a Derived Table (or a CTE) first and then join
untested:
SELECT
COALESCE(dt.Years, YEAR(DATEADD(MONTH, -Months.MonthID, GETDATE()))),
Months.[MonthName],
COALESCE(dt.totalReg, 0)
FROM
Months WITH(NOLOCK)
LEFT OUTER JOIN
(
SELECT
YEAR(DateRegistered) AS Years,
MONTH(DateRegistered) AS Mon,
COUNT(UserID)AS totalReg
FROM UserProfile WITH(NOLOCK)
WHERE DateRegistered > DATEADD(MONTH, -12,GETDATE())
GROUP BY
YEAR(DateRegistered),
MONTH(DateRegistered)
) AS dt
ON Months.MonthID = dt.mon
ORDER BY 1, Months.MonthID
I changed the order to Months.MonthID instead of MonthName and i added year because you might have august 2012 and 2013 in your result.