sql query to dynamically add fiscal month using pivot - sql

ALTER PROCEDURE [dbo].[_sp_GetDMActivityTrackerReport]
#CoachId VARCHAR(7),
#Month INT,
#FiscalYear INT
AS
BEGIN
INSERT #FiscalMonth (ID,Month,NbHolidays,MonthDate,TotalDays)
EXECUTE dbo._sp_GetFiscalMonths #Month, #FiscalYear
SELECT PreparationID,CoachId,UserID, MemberID,
[Rep Name], isnull(April,0) April, isnull(May,0) May, isnull(June,0)June,
isnull(July,0) July, isnull(August,0) August, isnull(September,0) September,
isnull(October,0) October, isnull(November,0) November,
isnull(December,0) December, isnull(January,0) January, isnull(February,0) February,
isnull(March,0) March,isnull((isnull(November,0) + isnull(December,0) +
isnull(January,0) + isnull(February,0) + isnull(March,0) + isnull(April,0) +
isnull(May,0) + isnull(June,0) + isnull(July,0) + isnull(August,0) +
isnull(September,0) + isnull(October,0)),0) as [Total Field TIME]
FROM
(
SELECT up.PreparationID,tt.UserId [CoachId],up.UserID, utm.MemberID,
(ui.FirstName + ' ' + ui.LastName) AS [Rep Name],DateName(Month,nft.MonthPeriodStart) [Month], sum(nft.Quantity) [Days]
FROM TransferedTime tt
INNER JOIN UPreparation up ON tt.PreparationID = up.PreparationID
RIGHT JOIN UTeamMembers utm ON tt.UserId = utm.CoachID AND utm.MemberID = up.UserID
INNER JOIN UserInfo ui ON utm.MemberID = ui.UserID
LEFT JOIN NonFieldTime nft ON nft.UserId = tt.UserId
AND tt.MonthPeriodFrom = nft.MonthPeriodStart
AND datename(Month,nft.MonthPeriodStart) + '-'+ substring(datename(Year,nft.MonthPeriodStart),3,2) IN
(SELECT Month +'-' +substring(datename(Year,MonthDate),3,2) [Months] FROM #FiscalMonth)
WHERE utm.MemberID IN (SELECT MemberID FROM UTeamMembers WHERE CoachID = #CoachId)
GROUP BY up.PreparationID,tt.UserId,up.UserID, utm.MemberID,
(ui.FirstName + ' ' + ui.LastName),DateName(Month,nft.MonthPeriodStart)) src
pivot
(
sum(Days)
for Month in (April, May, June, July, August, September, October,November, December, January, February, March)
)
piv
#Fiscalmonth returns:
Id, Month, NbHolidays, MonthDate
1 April 1 4/1/2012
2 May 2 5/1/2012
3 June 3 6/1/2012
4 July 4 7/1/2012
5 August 5 8/1/2012
6 September 6 9/1/2012
7 October 7 10/1/2012
8 November 8 11/1/2012
9 December 9 12/1/2012
10 January 10 1/1/2013
11 February 11 2/1/2013
12 March 12 3/1/2013
I have a stored procedure which generate report according to the fiscal year.
here fiscal year and fiscal month comes from the database now I am facing problem in generating this report dynamically as you can see i had fixed the months year which is not a good practice i want it to some way that if i changed the fiscal month in the database then my report reflect accordingly.

You will need to use dynamic SQL to do this. The rough code is going to be similar to this:
ALTER PROCEDURE [dbo].[_sp_GetDMActivityTrackerReport]
#CoachId VARCHAR(7),
#Month INT,
#FiscalYear INT
AS
BEGIN
INSERT #FiscalMonth (ID,Month,NbHolidays,MonthDate,TotalDays)
EXECUTE dbo._sp_GetFiscalMonths #Month, #FiscalYear
DECLARE #cols AS NVARCHAR(MAX),
#colsNull AS NVARCHAR(MAX),
#colsSum AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(DateName(Month,nft.MonthPeriodStart))
from NonFieldTime nft
where datename(Month,nft.MonthPeriodStart) + '-'+ substring(datename(Year,nft.MonthPeriodStart),3,2)
IN (SELECT Month +'-' +substring(datename(Year,MonthDate),3,2) [Months]
FROM +#FiscalMonth)
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
select #colsNull = STUFF((SELECT distinct ', IsNull(' + QUOTENAME(DateName(Month,nft.MonthPeriodStart))+', 0) as '+DateName(Month,nft.MonthPeriodStart)
from NonFieldTime nft
where datename(Month,nft.MonthPeriodStart) + '-'+ substring(datename(Year,nft.MonthPeriodStart),3,2)
IN (SELECT Month +'-' +substring(datename(Year,MonthDate),3,2) [Months]
FROM +#FiscalMonth)
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')
select #colsSum = STUFF((SELECT distinct '+ IsNull(' + QUOTENAME(DateName(Month,nft.MonthPeriodStart))+', 0)'
from NonFieldTime nft
where datename(Month,nft.MonthPeriodStart) + '-'+ substring(datename(Year,nft.MonthPeriodStart),3,2)
IN (SELECT Month +'-' +substring(datename(Year,MonthDate),3,2) [Months]
FROM +#FiscalMonth)
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')+' as [Total Field TIME] '
set #query = 'SELECT PreparationID,CoachId,UserID, MemberID,
[Rep Name], ' + #colsNull + ', '+ #colsSum+'
from
(
SELECT up.PreparationID,
tt.UserId [CoachId],
up.UserID, utm.MemberID,
(ui.FirstName + '' '' + ui.LastName) AS [Rep Name],
DateName(Month,nft.MonthPeriodStart) [Month],
sum(nft.Quantity) [Days]
FROM TransferedTime tt
INNER JOIN UPreparation up
ON tt.PreparationID = up.PreparationID
RIGHT JOIN UTeamMembers utm
ON tt.UserId = utm.CoachID AND utm.MemberID = up.UserID
INNER JOIN UserInfo ui
ON utm.MemberID = ui.UserID
LEFT JOIN NonFieldTime nft
ON nft.UserId = tt.UserId
AND tt.MonthPeriodFrom = nft.MonthPeriodStart
AND datename(Month,nft.MonthPeriodStart) + ''-''+ substring(datename(Year,nft.MonthPeriodStart),3,2) IN
(SELECT Month +''-'' +substring(datename(Year,MonthDate),3,2) [Months]
FROM +#FiscalMonth)
WHERE utm.MemberID IN (SELECT MemberID
FROM UTeamMembers
WHERE CoachID = '+#CoachId+')
GROUP BY up.PreparationID,tt.UserId,up.UserID, utm.MemberID,
(ui.FirstName + '' '' + ui.LastName),DateName(Month,nft.MonthPeriodStart)
) x
pivot
(
sum(Days)
for Month in (' + #cols + ')
) p '
execute(#query)
My suggestion instead of using the temp table #FiscalMonth is to create a table that is permanent for this. It will be much simpler to query against a perm table rather than the temp table when using dynamic sql. The temp table might be out of scope for the dynamic query.

There is PIVOT XML option in Oracle that allows you to avoid hard coding. I'm not sure if there is such option in SQL Server. You can write smth lk this - also Oracle query:
SELECT count(decode(state,'FL',custid)) "FL"
, count(decode(state,'NY',custid)) "NY"
, count(decode(state,'CA',custid)) "CA"
FROM scott.customer
GROUP BY state
/
Replace state with month etc...
Output:
FL NY CA
--- --- ---
0 2 0
5 0 8
Oracle example:
http://www.oracle-base.com/articles/11g/pivot-and-unpivot-operators-11gr1.php
SELECT * FROM
(
SELECT product_code, quantity
FROM pivot_test
)
PIVOT XML
(
SUM(quantity) AS sum_quantity
FOR (product_code) IN (SELECT DISTINCT product_code
FROM pivot_test
WHERE id < 10)
)
/

Related

How to properly create a Dynamic Pivot Function in SQL? [duplicate]

This question already has answers here:
Group by column and multiple Rows into One Row multiple columns
(2 answers)
Closed 8 months ago.
I come back with my problem. I've been trying to create a dynamic pivot procedure but I have some trouble while creating the columns.
Example:
TranID
Bank Name
NetAmount
Account
01
StormWindBank
23.0$
A
02
StormWindBank
14.0$
B
03
StormWindBank
00.0$
A
04
StormWindBank
12.0$
B
Result intended :
Account
Bank Name
NetAmount
NetAmount2
A
StormWindBank
23.0$
00.0$
B
StormWindBank
14.0$
12.0$
SELECT DISTINCT [Account]
,[Currency]
,TranID
,[Bank Code]
,[Bank Name]
,[Client No_]
,[NetAmount]
INTO #TEMP
FROM [My Table]
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.NetAmount)
FROM #TEMP c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [Account], [Bank Name], ' + #cols + ' from
(
select [Account],TranID, NetAmount, [Bank Name]
from #TEMP
) x
pivot
(
max(TranID)
for NetAmount in (' + #cols + ')
) p '
So far, I have the result below:
Account
Bank Name
23.0$
00.0$
A
StormWindBank
1
2
B
StormWindBank
3
4
The 1, 2, 3, 4 are the TranID.
I'm close but I have been struggling to fix that, anyone have an idea ?
Thank you!
You need to PIVOT off a derived column using row_number()
Example
Declare #SQL varchar(max) = stuff( ( Select Distinct concat(',[NetAmount',row_number() over (partition by [Bank Name],[Account] order by [TranID]),']' )
From #TEMP For XML Path('') ),1,1,'')
Set #SQL = '
Select *
From (
Select [Account]
,[Bank Name]
,Item = concat(''NetAmount'',row_number() over (partition by [Bank Name],[Account] order by [TranID]) )
,Value = [NetAmount]
from #TEMP
) src
Pivot ( max( [Value] ) for Item in ('+ #SQL +') ) pvt
'
Exec(#SQL)
Results
Account Bank Name NetAmount1 NetAmount2
A StormWindBank 23.0$ 00.0$
B StormWindBank 14.0$ 12.0$

Getting the counts of the number of months a user has been using a particular service in SQL

I have data like below:
user_id month_id service
895 201612 S
262 201612 V
5300 201612 BB
Now there can be users who have used more than one service in a year, and I would like to have a query which gives me that. For example say 895 has used S for 3 months and BB for 4 months and then his latest service is V. So :
user_id S BB V
895 3 4 5
How do I do this pivot and count in SQL , can someone please help me?
Here is how you would do it dynamically:
DECLARE #Cols AS NVARCHAR(MAX)
,#Query AS NVARCHAR(MAX);
SET #Cols = STUFF((
SELECT DISTINCT ',' + QUOTENAME(service)
FROM YourTable
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #Query = 'SELECT user_id, ' + #Cols + ' from
(
select user_id
, month_id
, service
from YourTable
) x
pivot
(
COUNT(month_id)
for service in (' + #Cols + ')
) p '
EXECUTE(#Query)
Edit: Didn't realize it was a COUNT of month_id and not DATEDIFF(mm, CONVERT(DATETIME, month_id + ''01'',GETDATE()). Updated.

Pivot SQL table

I have a table with a column "date" and "name".
Here you can find a sample of my table: http://sqlfiddle.com/#!2/eddede/1
What I need to do is to count how many rows a person has for every year. If the person doesn't have a value in year X, you must see 0. The order of the names must be on total count of current year DESC, so at the moment: 2014.
I can count for every year/name, but not sure I need this for the pivot table: http://sqlfiddle.com/#!2/eddede/3
I would like that the result should look like this:
NAME 2012 2013 2014 TOTAL
Person B 2 2 2 6
Person C 0 1 2 3
Person A 4 3 1 8
I've tried, but I get something like this (So I don't post the wrong SQL-query...)
2012 2013 2014
6 6 5
You can get the result using an aggregate function with some conditional logic like a CASE expression:
select
name,
sum(case when year(date) = 2012 then 1 else 0 end) [2012],
sum(case when year(date) = 2013 then 1 else 0 end) [2013],
sum(case when year(date) = 2014 then 1 else 0 end) [2014],
count(*) Total
from list
group by name;
See SQL Fiddle with Demo
Now, if the year values are unknown, then you will need to use dynamic SQL. This creates a string of the SQL that you need to execute:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#orderby nvarchar(max)
select #cols
= STUFF((SELECT ',' + QUOTENAME(year(date))
from list
group by year(date)
order by year(date)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #orderby = 'ORDER BY ['+cast(year(getdate()) as varchar(4)) + '] desc'
set #query = 'SELECT name, ' + #cols + ', Total
from
(
select name, year(date) dt,
count(*) over(partition by name) Total
from list
) x
pivot
(
count(dt)
for dt in (' + #cols + ')
) p '+ #orderby
exec sp_executesql #query;
See SQL Fiddle with Demo
I think this is what you are looking for:
SELECT
name,
SUM(if(year(date) = 2012, 1, 0)) AS '2012',
SUM(if(year(date) = 2013, 1, 0)) AS '2013',
SUM(if(year(date) = 2014, 1, 0)) AS '2014'
FROM list
GROUP BY name;
and with total :)
SELECT
t.name,
t.`2012`,
t.`2013`,
t.`2014`,
t.`2012` + t.`2013` + t.`2014` as total
FROM
(
SELECT
name,
SUM(if(year(date) = 2012, 1, 0)) AS `2012`,
SUM(if(year(date) = 2013, 1, 0)) AS `2013`,
SUM(if(year(date) = 2014, 1, 0)) AS `2014`
FROM list
GROUP BY name
) as t

Error in Dynamic PIVOT Table in SQL

I am getting error in Dynamic PIVOT Table.
I have a Tables like
TT_Child
TTChild_ID TT_ID Roll_No Std_Reg_ID Attendance
1 3 1 22 1
2 3 2 23 0
and Table
TT_Master
TT_ID Attend_date Time_from Time_To Course_ID Faculty_ID Acad_Year Subject_ID
1 2014-03-01 10:00 11:00 1 16 2013-2014 34
2 2014-03-02 10:00 11:00 1 16 2013-2014 34
3 2014-03-03 10:00 11:00 1 16 2013-2014 34
Student_Registration_Master
Std_Reg_ID Stud_FNAME stud_MNAME Stud_MNAME
--I am using this PIVOT query but getting this error. I have to use Attend_Date and Time as a Column Name.
Create PROCEDURE [dbo].[Attendance_Test]
#courseid as int=null, #acadyear nvarchar(15)=null
AS
Declare #colList varchar(max)
Declare #qry varchar(max)
SET #colList = STUFF((SELECT distinct ',' + QUOTENAME(CONVERT(VARCHAR(19), TTM.Attend_Date, 103)) + ' '+ QUOTENAME(TTM.Time_From)
FROM TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
where (TTM.Course_ID = #courseid)
FOR XML PATH(''), TYPE).value('/', 'NVARCHAR(MAX)') ,1,1,'')
SET #qry = 'SELECT SA.Reg_ID, STUD_FNAME + STUD_MNAME + STUD_LNAME as [Student Name], '+#colList+'
FROM
(
select SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date , SA.Attendance from TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
inner join STUDENT_Registration_MASTER SR on CR.Reg_ID = SR.STUD_Reg_ID
where (TTM.Course_ID = '+cast(#courseid as varchar(50))+ ') and (TTM.Acad_Year = '''+#acadyear+''')
group by SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date, SA.Attendance
) as s
PIVOT
(
MAX(Attendance) FOR Attend_Date IN (' + #colList + ')
) pvt'
print(#qry)
Exec(#qry)
-- I am getting this error message
SELECT SA.Reg_ID, STUD_FNAME + STUD_MNAME + STUD_LNAME as [Student Name], [01/03/2014] [10:00:00.0000000],[02/03/2014] [10:00:00.0000000],[03/03/2014] [10:00:00.0000000],[05/03/2014] [10:00:00.0000000],[05/03/2014] [11:00:00.0000000]
FROM
(
select SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date , SA.Attendance from TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
inner join STUDENT_Registration_MASTER SR on CR.Reg_ID = SR.STUD_Reg_ID
where (TTM.Course_ID = 1) and (TTM.Acad_Year = '2013-2014')
group by SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date, SA.Attendance
) as s
PIVOT
(
MAX(Attendance) FOR Attend_Date IN ([01/03/2014] [10:00:00.0000000],[02/03/2014] [10:00:00.0000000],[03/03/2014] [10:00:00.0000000],[05/03/2014] [10:00:00.0000000],[05/03/2014] [11:00:00.0000000])
) pvt
Error Message
Msg 102, Level 15, State 1, Line 12
Incorrect syntax near '10:00:00.0000000'.
Plz give solution
When you review this line of your code you will see the error:
MAX(Attendance)
FOR Attend_Date IN ([01/03/2014] [10:00:00.0000000],[02/03/2014] [10:00:00.0000000],
[03/03/2014] [10:00:00.0000000],[05/03/2014] [10:00:00.0000000],
[05/03/2014] [11:00:00.0000000])
The date and the time have each been wrapped in square brackets separately so you will get an error.
You need to concatenate the date and time together first, then wrap in square brackets using QUOTENAME. You should be able to change the code to:
SET #colList
= STUFF((SELECT distinct ',' + QUOTENAME(CONVERT(VARCHAR(19), TTM.Attend_Date, 103) + ' '+ TTM.Time_From)
FROM TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
where (TTM.Course_ID = #courseid)
FOR XML PATH(''), TYPE).value('/', 'NVARCHAR(MAX)') ,1,1,'')
See SQL Fiddle with Demo of your version and this new version.

SQL Server PIVOT with multiple X-axis columns

Take the following example data:
Payroll Forname Surname Month Year Amount
0000001 James Bond 3 2011 144.00
0000001 James Bond 6 2012 672.00
0000001 James Bond 7 2012 240.00
0000001 James Bond 8 2012 1744.50
0000002 Elvis Presley 3 2011 1491.00
0000002 Elvis Presley 6 2012 189.00
0000002 Elvis Presley 7 2012 1816.50
0000002 Elvis Presley 8 2012 1383.00
How would i PIVOT this on the Year + Month (eg: 201210) but preserve Payroll, Forename & Surname as seperate columns, for example, the above would become:
Payroll Forename Surname 201103 201206 201207 201208
0000001 James Bond 144.00 672.00 240.00 1744.50
0000002 Elvis Presley 1491.00 189.00 1816.50 1383.00
I'm assuming that because the Year + Month names can change then i will need to employ dynamic SQL + PIVOT - i had a go but couldnt even get the code to parse, nevermind run - any help would be most appreciated!
Edit: What i have so far:
INSERT INTO #tbl_RawDateBuffer
( PayrollNumber ,
Surname ,
Forename ,
[Month] ,
[Year] ,
AmountPayable
)
SELECT PayrollNumber ,
Surname ,
Forename ,
[Month] ,
[Year] ,
AmountPayable
FROM RawData
WHERE [Max] > 1500
DECLARE #Columns AS NVARCHAR(MAX)
DECLARE #StrSQL AS NVARCHAR(MAX)
SET #Columns = STUFF((SELECT DISTINCT
',' + QUOTENAME(CONVERT(VARCHAR(4), c.[Year]) + RIGHT('00' + CONVERT(VARCHAR(2), c.[Month]), 2))
FROM #tbl_RawDateBuffer c
FOR XML PATH('') ,
TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #StrSQL = 'SELECT PayrollNumber, ' + #Columns + ' from
(
select PayrollNumber
, CONVERT(VARCHAR(4), [Year]) + RIGHT(''00'' + CONVERT(VARCHAR(2), [Month]), 2) dt
from #tbl_RawDateBuffer
) x
pivot
(
sum(AmountPayable)
for dt in (' + #Columns + ')
) p '
EXECUTE(#StrSQL)
DROP TABLE #tbl_RawDateBuffer
Ok, as you said, you are gonna need dynamic SQL, so first go to this link. Once you read that, try the following:
UPDATED CODE FOLLOWING COMMENT:
DECLARE #cols AS NVARCHAR(MAX), #cols2 AS NVARCHAR(MAX), #query AS NVARCHAR(MAX);
WITH CTE AS
(
SELECT *, CAST([Year] AS NVARCHAR(4))+RIGHT('00'+CAST([Month] AS NVARCHAR(2)),2) YearMonth
FROM YourTable
)
SELECT #cols = STUFF(( SELECT DISTINCT ',' + QUOTENAME(YearMonth)
FROM CTE
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''),
#cols2 = STUFF(( SELECT DISTINCT ',ISNULL(' + QUOTENAME(YearMonth) + ',0) AS ' + QUOTENAME(YearMonth)
FROM CTE
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = '
SELECT Payroll, Forname, Surname, ' + #cols2 + '
FROM ( SELECT Payroll, Forname, Surname,
CAST([Year] AS NVARCHAR(4))+RIGHT(''00''+CAST([Month] AS NVARCHAR(2)),2) YearMonth,
Amount
FROM YourTable ) T
PIVOT(SUM(Amount) FOR YearMonth IN ('+#cols+')) PT'
EXEC(#Query)
select payroll, forname,surname,[20113] as [201103], [20126] as [201206], [20127] as [201207], [20128] as [201208]
from
(select Payroll, Forname, Surname, YEAR+MONTH as d, Amount from pivot1) up
pivot(sum(up.Amount) for up.d in ([20113], [20126], [20127], [20128])) as pvt