SQL Server 2014 pivot table row and column month grant total - sql-server

How can I add row and column grand totals in the table below?
I want to get total and grand total by months.
However, I couldn't do that.
SELECT *
FROM(
SELECT
YEAR(DueDate) [Year],
CASE MONTH(DueDate)
WHEN 1 THEN 'January'
WHEN 2 THEN 'February'
WHEN 3 THEN 'March'
WHEN 4 THEN 'April'
WHEN 5 THEN 'May'
WHEN 6 THEN 'June'
WHEN 7 THEN 'July'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'October'
WHEN 11 THEN 'November'
WHEN 12 THEN 'December'
END as [Month],
ProductID,
OrderQty
FROM Production.WorkOrder
) WorkOrders
PIVOT
(
SUM(OrderQty)
FOR [Month] IN (
[January],[February],[March],[April],
[May],[June],[July],[August],
[September],[October],[November],[December]
)
) AS PivotTable
ORDER BY [Year], ProductID

Stuff like this is far easier using a conditional aggregate over the restrictive PIVOT operator.
Without sample data, nor expected results, this isn't tested, but you should be able to achieve what you're after with something like this:
SELECT CASE WHEN GROUPING(DATEPART(YEAR,DueDate)) = 0 THEN
CAST(DATEPART(YEAR,DueDate) AS varchar(50))
ELSE 'GrandTotal' END AS [Year],
SUM(CASE DATEPART(MONTH,DueDate) WHEN 1 THEN OrderQty END) AS January, --Don't use single quotes for alaises,
SUM(CASE DATEPART(MONTH,DueDate) WHEN 2 THEN OrderQty END) AS Feburary, --it can be very confusing to read.
SUM(CASE DATEPART(MONTH,DueDate) WHEN 3 THEN OrderQty END) AS March, --Single quotes are for literal strings.
SUM(CASE DATEPART(MONTH,DueDate) WHEN 4 THEN OrderQty END) AS April, --Using ' for alias only work in the SELECT too,
SUM(CASE DATEPART(MONTH,DueDate) WHEN 5 THEN OrderQty END) AS May, --something like ORDER BY 'January' would not
SUM(CASE DATEPART(MONTH,DueDate) WHEN 6 THEN OrderQty END) AS June, --order by data by the column aliases as 'January'.
SUM(CASE DATEPART(MONTH,DueDate) WHEN 7 THEN OrderQty END) AS July,
SUM(CASE DATEPART(MONTH,DueDate) WHEN 8 THEN OrderQty END) AS August,
SUM(CASE DATEPART(MONTH,DueDate) WHEN 9 THEN OrderQty END) AS September,
SUM(CASE DATEPART(MONTH,DueDate) WHEN 10 THEN OrderQty END) AS October,
SUM(CASE DATEPART(MONTH,DueDate) WHEN 11 THEN OrderQty END) AS November,
SUM(CASE DATEPART(MONTH,DueDate) WHEN 12 THEN OrderQty END) AS December,
SUM(OrderQty) AS GrandTotal,
ProductID
FROM Production.WorkOrder
GROUP BY GROUPING SETS(
(DATEPART(YEAR, DueDate), ProductID),
(DATEPART(YEAR, DueDate)),
()
);

Related

Running total in SQL Server isn’t calculating correctly

I am trying to achieve the following running total as shown below. Where a2013 is the number of enquiries from that month and 2013c is the running total.
Month a2013 2013C
---------------------
Jan 1 1
Feb 3 4
March 2 6
In a similar post it advised to add a partition which I have done but the returned value is still incorrect.
with years as (
select datename(month,p.enquirydate) as Month, count(*) as count,
count(case when p.enquirydate between '2013-01-01' and '2013-12-31
23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2013,
count(case when p.enquirydate between '2014-01-01' and '2014-12-31
23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2014,
count(case when p.enquirydate between '2015-01-01' and '2015-12-31
23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2015,
count(case when p.txtenquirydate between '2016-01-01' and '2016-12-31
23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2016,
count(case when p.enquirydate between '2017-01-01' and '2017-12-31
23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2017
from customers p
group by datename(month,p.enquirydate), datepart(month,p.enquirydate)
)
select y.Month,a2013, sum(a2013) over(partition by y.month) as '2013C'
from years y
Month a2013 2013C
NULL 0 0
April 4 4
August 1 1
December 4 4
February 3 3
January 1 1
July 3 3
June 3 3
March 2 2
May 1 1
November 4 4
October 3 3
September 4 4
Maybe following query can help you to implement a SQL Running Sum solution on SQL Server with SUM() and UnBounded Preceding clause
I once used for a stock aging problem
declare #t as table (Id int, Month varchar(100), a2013 int)
insert into #t values
(1,NULL ,0),
(4,'April', 4 ),
(8,'August', 1 ),
(12,'December', 4 ),
(2,'February', 3 ),
(1,'January', 1 ),
(7,'July', 3 ),
(6,'June', 3 ),
(3,'March', 2 ),
(5,'May', 1 ),
(11,'November', 4 ),
(10,'October', 3 ),
(9,'September', 4 )
select
*,
sum([a2013]) over (order by Id rows unbounded preceding) as [2013c]
from #t
order by Id
As you can see the last column is showing sum of rows displayed before
with years as (
select DATEPART(month,enquirydate) as MonthNumber, count(*) as count,
count(case when p.enquirydate between '2013-01-01' and '2013-12-31 23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2013,
count(case when p.enquirydate between '2014-01-01' and '2014-12-31 23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2014,
count(case when p.enquirydate between '2015-01-01' and '2015-12-31 23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2015,
count(case when p.enquirydate between '2016-01-01' and '2016-12-31 23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2016,
count(case when p.enquirydate between '2017-01-01' and '2017-12-31 23:59:59.997' and EnrolmentYear ='2018' then datename(month,p.enquirydate)
else null end) as a2017
from #customers p
group by DATEPART(month,enquirydate)
)
select datename(month,dateadd(month, MonthNumber - 1, 0)), a2013, sum(a2013) over(Order By y.MonthNumber ROWS UNBOUNDED PRECEDING) as '2013C'
from years y

Select rolling totals by month more data

I have a query that I have been using to track users sales. Previously they ere required to make a quota each month. Now, however they would like to change the rule to allow them to start any month, so they may go from June to June or whatever. they also want users to start over immediately if they miss a month. This does seem to be a more equitable system because if they didn't make the quota in March, for example, they were unable to count any they made after that month for the entire year. This really messes up my query though, and I don't know how to fix it. anyone have a solution?
here is the existing t-sql.
#Year int
AS
BEGIN
DECLARE #DateStart datetime
DECLARE #DateStop datetime
SELECT #DateStart = CONVERT(DATETIME, CONVERT( char(4), #Year) + '-01- 01')
SELECT #DateStop = CONVERT(DATETIME, CONVERT( char(4), #Year + 1) + '-01- 01')
SET NOCOUNT ON;
SELECT r.riderid,
r.dname,
DATEPART(yyyy, m.ridedate),
SUM(CASE DATEPART(mm, m.datesale) WHEN 1 THEN m.quota ELSE 0 END) AS [jan],
SUM(CASE DATEPART(mm, m.datesale) WHEN 2 THEN m.quota ELSE 0 END) AS [feb],
SUM(CASE DATEPART(mm, m.datesale) WHEN 3 THEN m.quota ELSE 0 END) AS [mar],
SUM(CASE DATEPART(mm, m.datesale) WHEN 4 THEN m.quota ELSE 0 END) AS [apr],
SUM(CASE DATEPART(mm, m.datesale) WHEN 5 THEN m.quota ELSE 0 END) AS [may],
SUM(CASE DATEPART(mm, m.datesale) WHEN 6 THEN m.quota ELSE 0 END) AS [jun],
SUM(CASE DATEPART(mm, m.datesale) WHEN 7 THEN m.quota ELSE 0 END) AS [jul],
SUM(CASE DATEPART(mm, m.datesale) WHEN 8 THEN m.quota ELSE 0 END) AS [aug],
SUM(CASE DATEPART(mm, m.datesale) WHEN 9 THEN m.quota ELSE 0 END) AS [sep],
SUM(CASE DATEPART(mm, m.datesale) WHEN 10 THEN m.quota ELSE 0 END) AS [oct],
SUM(CASE DATEPART(mm, m.datesale) WHEN 11 THEN m.quota ELSE 0 END) AS [nov],
SUM(CASE DATEPART(mm, m.datesale) WHEN 12 THEN m.quota ELSE 0 END) AS [dec],
SUM(m.quota) as [tot]
FROM users u
JOIN mysales m
ON m.riderid = u.riderid
Where m.datesale Between #DateStart AND #DateStop
GROUP BY DATEPART(yyyy, m.datesale), u.userid
ORDER BY DATEPART(yyyy, m.datesale), SUM(m.quota) DESC
END
OK -here is data
The table holds the users id, the customer id , amount of sale and date of sale
The query pulls the user, the sum of sales by month. User 250 made quota in July/2016, but did not in August, so he should get an entry in #quota for July, and September, but because he did not in August he has to restart in September; user# 300 has made quota from Jan 2016 to SEPT so he has qualified for his bonus as long as he finishes the 12 months. User 350 has successfully finished the year and should get a bonus. at this time in prod there is no quota table, would that simplify? What I need is a list of users that are in the running.
--drop table #sales
--drop table #quota
create table #sales
(
--saleid int --PK
userid int -- salesperson FK
, customerid int --FK
, sale_amt decimal
, date_sale datetime
)
insert into #sales values
(300,1301,542.90,'3-2-2016'),
(300,1301,782.70,'3-4-2016'),
(300,1541,600.70,'3-7-2016'),
(300,903,640.71,'3-10-2016'),
(300,1745,900.01,'3-29-2016'),
(300,1440,2040.71,'2-10-2016'),
(300,903,640.71,'2-20-2016'),
(300,414,1489.00,'1-18-2016'),
(300,1645,1322.00,'1-20-2016'),
(300,1200,1156.09,'4-2-2016'),
(300,1204,1456.00,'4-20-2016'),
(250,1140,156.89,'4-12-2016'),
(250,1240,1176.69,'4-14-2016'),
(250,840,480.61,'4-17-2016'),
(250,1940,500.71,'5-17-2016'),
(250,1425,4800.61,'6-1-2016'),
(250,1840,701.32,'6-15-2016'),
(250,1840,701.32,'7-15-2016'),
(250,1840, 2701.32,'8-15-2016'),
(450,8421,2500.61,'7-17-2015'),
(450,8422,2500.1,'8-17-2015'),
(450,843,2500.1,'9-17-2015'),
(450,8431,2500.00,'10-17-2015'),
(450,1431,2500.00,'11-17-2015'),
(450,4311,2500.00,'12-17-2015'),
(450,4310,2500.00,'1-17-2016'),
(450,1310,2500.00,'2-17-2016'),
(450,1310,2500.00,'3-17-2016'),
(450,130,2500.00,'4-17-2016'),
(450,1130,2500.00,'5-17-2016'),
(450,113,2500.00,'6-17-2016')
Select userid
, sum(sale_amt) Sale
, DATEPART(mm,date_sale) as[month]
from #sales
group by userid, DATEPART(mm,date_sale) order by userid
create table #quota
(
qid int --PK
, userid int -- salesperson FK
, quota bit -- awarded when sales => $2500.00
, datesale datetime -- date quota made
)
Just one possible way to write a query that looks back #running_months number of months to verify that no quotas have been missed during the window for each user:
select userid from users u
where not exists (
select 1 from #sales s
where s.userid = u.userid
and date_sale > dateadd(month, -#running_months - 1, current_timestamp)
and datediff(month, sales_date, current_timestamp) between 1 and #running_months
group by month(sales_amt)
having sum(sales_amt) < 2500
)
EDIT: I later realized that you probably do have users with no sales during a month so you'll probably need to actually verify the condition that all the months are over quota rather than none of the months are under quota:
select userid from users u
where userid in (
select userid from
(
select userid from #sales s
where s.userid = u.userid
and date_sale > dateadd(month, -#running_months - 1, current_timestamp)
and datediff(month, sales_date, current_timestamp) between 1 and #running_months
group by month(sales_amt)
having sum(sales_amt) >= 2500
) q
group by userid
having count(*) = #running_months
)

How to add Totals in SQL

I am trying to get the totals of each month as of YTD (Years to date) Can someone please help me figure out how to integrate this in my query? thank you This is what I have so far.
DECLARE #Year int
set #Year = 2013
select a.first_name, a.last_name
, COUNT(CASE WHEN MONTH(b.Funded_date) = 1 THEN 1 ELSE NULL END) January
, COUNT(CASE WHEN MONTH(b.Funded_date) = 2 THEN 1 ELSE NULL END) February
, COUNT(CASE WHEN MONTH(b.Funded_date) = 3 THEN 1 ELSE NULL END) March
, COUNT(CASE WHEN MONTH(b.Funded_date) = 4 THEN 1 ELSE NULL END) April
From tContact a Join tContract b ON a.contact_id = b.contract_id
Group by a.first_name, a.last_name
This is just an example of how you could count up rows that fall under a certain month.
SELECT MONTH(b.Funded_date) AS 'MonthNum',
COUNT(*) AS 'Total'
FROM Table AS b
WHERE YEAR(b.Funded_date) = 2014
GROUP BY MONTH(b.Funded_date)
Hopefully this will help you with your query.
Thanks
What I tried to do here is create an upper bound record for each month in tContract then join that back into the query you already had. It is joined on dates that are between the beginning of the year and the current month.
DECLARE #Year int
set #Year = 2013
select Ms.thismonth, count(B.thing_You_are_Totalling) from (
select thisMonth = dateadd(month,datediff(month,0,Funded_date),0)
from tContract
where moid = 2005405
and year(Funded_date) = #Year
group by dateadd(month,datediff(month,0,Funded_date),0)
) Ms
inner join (select * from tContact a inner join tContract ON a.contact_id = tContract.contract_id) B
on B.Funded_date >=dateadd(year,datediff(year,0,B.Funded_date),0) -- beginning of year
and B.Funded_date <= Ms.thisMonth -- this month
where year(B.Funded_date) = #Year -- restrict to only this year
group by thisMonth, first_name, last_name
I don't have your full table definition so there might be some issues (maybe a SqlFiddle is in order)
Not sure if this is what you are asking for.
select a.first_name, a.last_name
, COUNT(CASE WHEN MONTH(b.Funded_date) = 1 THEN 1 ELSE NULL END) January
, COUNT(CASE WHEN MONTH(b.Funded_date) = 2 THEN 1 ELSE NULL END) February
, COUNT(CASE WHEN MONTH(b.Funded_date) = 3 THEN 1 ELSE NULL END) March
, COUNT(CASE WHEN MONTH(b.Funded_date) = 4 THEN 1 ELSE NULL END) April
, COUNT(*) TotalCount
, SUM(CASE WHEN MONTH(b.Funded_date) = 1 THEN Amount ELSE NULL END) JanuaryAmount
, SUM(CASE WHEN MONTH(b.Funded_date) = 2 THEN Amount ELSE NULL END) FebruaryAmount
, SUM(CASE WHEN MONTH(b.Funded_date) = 3 THEN Amount ELSE NULL END) MarchAmount
, SUM(CASE WHEN MONTH(b.Funded_date) = 4 THEN Amount ELSE NULL END) AprilAmount
From tContact a Join tContract b ON a.contact_id = b.contact_id
WHERE YEAR(b.Funded_date) = #Year
Group by a.first_name, a.last_name
How about this:
declare #year int = 2013
select a.first_name
, a.last_name
, month(b.Funded_date) [Month]
, datename(month, dateadd(month, month(date_of_birth_dt), - 1)) [MonthName]
, count(month(b.Funded_date)) [Total]
from tContact a
where a.[Year] = #year
group by a.first_name, a.last_name, month(b.Funded_date)
It returns the total of each Month for the year 2013. a.[Year] might not the the name of the field that you have so adjust accordingly. Also, [Month] returns numeric value for month.
Use the Count(*) As Total function. I'm sure this will help you
SELECT MONTH(b.Funded_date) AS 'MonthNum',
COUNT(*) AS 'Total'
FROM Table AS b
WHERE YEAR(b.Funded_date) = 2014
GROUP BY MONTH(b.Funded_date)

How to display month name in different columns in sql using datediff function

I have this query:
DECLARE #month INT
SET #month=1
SELECT
CLOI_ClientOrderItems.cl_Id,
NoOfInv = SUM(CASE WHEN DATEPART(mm, in_date_issued) <= #month
AND DATEPART(yyyy, in_date_issued) = 2014
THEN 1 ELSE 0 END),
MonthTotal = SUM(CASE WHEN DATEPART(mm, in_date_issued) <= #month
AND DATEPART(yyyy, in_date_issued) = 2014
THEN in_total ELSE 0 END),
Grandtotal = SUM(in_total),
RemainingAmount = SUM(in_total) - SUM(CASE
WHEN DATEPART(mm, in_date_issued) <= #month
THEN in_total ELSE 0 END)
FROM (SELECT
DISTINCT MasterOrderId, cl_Id
FROM
CLOI_ClientOrderItems) as CLOI_ClientOrderItems
INNER JOIN
IN_Invoices
ON
IN_Invoices.MasterOrderId = CLOI_ClientOrderItems.MasterOrderId
GROUP BY
CLOI_ClientOrderItems.cl_id
i want output like
noofinv |amt |clid | grandtotal | jan |feb |march |remainingamt
5 |7.00 |100000_Pri | 245.00 | 0.00 |238.00 |7.00 |238.00
12 |2510.12 |100001_pro | 181110.29 | 138891.92 |9708.25 |510.12 |178600.17
If I pass month number like 3, it should display it as like Jan Feb and March and its related records in the respective month.
Try this, where condition is useful for you.
select Datename(mm,in_date_issued) from IN_Invoices Where Datediff(yyyy,in_date_issued,3) = 2014 and Datediff(mm,in_date_issued,#monthParameter) <= 3
You can Try like This...
declare #MonthCount int=1
SELECT DATENAME(month, DATEADD(month, #MonthCount-1 ,
CAST('2014-01-01' AS datetime))) as Month_Name
OP:
Month_Name
January

Quickest way to get aggregate date statistics / pivot

I am tryig to write what must be a fairly common audit report; number of rows added to a table over time; reported back against previous cycles to understand the trends in the data.
I have a table that audits creation of rows in the database. It has a field RowEnteredDate date time. I am looking to create an audit report Week/ Month/ Current Quarter / Year.
In my head I am looking at this as multiple passes over the data around the dates; which is quite costly in my database. My reasoning at the moment is
I started out with working out the dates for my year / month / quarter
set datefirst 1
declare #dateranges table (
rangelabel varchar(100),
startdate datetime,
enddate datetime,
myrowcount integer identity(1,1)
)
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Year',
DATEADD(yy, DATEDIFF(yy,0,GETDATE()), 0),
DATEADD(ms,-3,DATEADD(yy, DATEDIFF(yy,0,GETDATE() )+1, 0))
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Quarter',
DATEADD(qq, DATEDIFF(qq,0,GETDATE()), 0),
DATEADD(qq, DATEDIFF(qq, - 1, getdate()), - 1)
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Month',
DATEADD(month, DATEDIFF(month, 0, getdate()), 0),
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
If my table is tblOfUsefullFacts and my date row is RowEnteredDate what is the best way to get the aggregate; broken by Day.
Date Range Mon Tues Wed Thu Fri Sat Sun
Year To date 12000 13000 12000 3200 98000 8900 4000
Quarter 1 302 407 201 97 1732 120 37
Month ...
I can get the totals by day easily enough using a query like this
select
count(*) ,
datepart(weekday, RowEnteredDate)
from
tblOfUsefullFacts aa
Where
datepart(weekday, RowEnteredDate) is not null
group by datepart(weekday, RowEnteredDate)
order by datepart(weekday, RowEnteredDate) sac
This selects the data out row by row; which i could pivot and loop round to get the data. Im slightly nervous as the real numbers are in the 10's of millions in them and would like to not impact the underlying processing if i can avoid it.
As i need to do this in multiple passes is there a lighter way to do this without running the loops to get the totals? Or a mechanism in SQL my fuzzy brain is ignoring.
This should give you an idea how to do it. Sorry for any syntax errors, it isn't tested.
;with cte as
(
select
d.rangelabel,
datepart(weekday, RowEnteredDate) as WkDay,
count(*) as RowCt
from tblOfUsefullFacts f
join #dateranges d on f.RowEnteredDate between d.startdate and d.enddate
Where datepart(weekday, RowEnteredDate) is not null
group by d.rangelabel,datepart(weekday, RowEnteredDate)
)
select
RangeLabel,
sum(case when WkDay = 1 then RowCt else 0 end) as Sunday,
sum(case when WkDay = 2 then RowCt else 0 end) as Monday,
sum(case when WkDay = 3 then RowCt else 0 end) as Tuesday,
sum(case when WkDay = 4 then RowCt else 0 end) as Wednesday,
sum(case when WkDay = 5 then RowCt else 0 end) as Thursday,
sum(case when WkDay = 6 then RowCt else 0 end) as Friday,
sum(case when WkDay = 7 then RowCt else 0 end) as Saturday
from cte
group by RangeLabel

Resources