Pivot with dates of given month - sql

I am working with a view I'm trying to process and present in a more usable way using SQL Server.
The data:
select *
from vReportData
where date between '01.01.2020' and '31.01.2020'
which looks like this:
persnr date abw target attendance
----------------------------------------------------
000001 2020-01-01 5.00 4.45
000001 2020-01-02 0.00 8.00
000001 2020-01-04 6.00 7.00
000001 2020-01-30 6.00 6.00
000001 2020-01-31 6.00 6.50
[...]
999999 2020-01-02 U 5.00 0.00
999999 2020-01-30 4.00 5.00
999999 2020-01-31 5.00 5.00
The desired output:
persnr 01.01.2020 02.01.2020 [...] 30.01.2020 31.01.2020 sum
-----------------------------------------------------------------------
000001 4.45/5.00 8.00/0.00 [...] 6.00/6.00 6.50/6.00 xxx
999999 U [...] 5.00/4.00 0.00/5.00 xxx
Basically I would like to transform the table to have one line per person and have the dates as column. As for the abw, target and attendance I want to get them to be displayed by the date it happened.
The idea
I'm very likely need to use pivot function to get the table shape I want. Since I don't have all dates of a given month in my vReportData I thought about generating them first using something along the lines of:
declare #start datetime = '2020-05-01'
declare #ende datetime = '2020-05-31'
;with calender AS
(
select #start as Datum
union all
select Datum + 1 from Calender
where Datum + 1 <= #ende
)
select [Date] = convert(varchar, Datum, 104), 0.00 as value
from calender
to get the dates of the given month as rows.
Adding the persnr I ended up with:
select distinct
#vReportData.persnr,
[Date] = convert(varchar, Datum, 104)
from
calender
cross join
#vReportData
I don't how how to get the pivot working using my approach.
My complete SQL + attempt can be found here - for testing purpose I ended up changing the end date at some point.
https://data.stackexchange.com/stackoverflow/query/1240742/reportdata
additon & followup:
I was trying a few other attemts. Here's another issue I encountered:
I ended up using a dynamic pivot where I was first selecting the dates and then adding them to the pivot. I have the desired table form, but not one line per persnr.
declare #dynQuery as nvarchar(MAX)
declare #cols as nvarchar(MAX)
select #cols= isnull(#cols + ',','')
+ quotename(convert(varchar, date, 104))
from (select distinct date from app_hours
where date between '01.01.2020' and '31.01.2020'
) AS Dates
SET #dynQuery =
N'select distinct persnr, ' + #cols + '
from app_hours
pivot (
sum(attendancetime)
for date in (' + #cols + '))
AS pivot where persnr = 000001'
EXEC sp_executesql #dynQuery
the table looks like this:
persnr 01.01.2020 02.01.2020 03.01.2020 [...]
----------------------------------------------------------------
000001 NULL NULL 5.00 ...
000001 NULL 5.00 NULL ...
000001 5.00 NULL NULL ...

Here is a way to add a floating subtotal placeholder to be offset from the last column of real data. This does not solve the column headers problem that was solved above, however, you could either create a SP to produce your headers laid out similarly or convert the pivot part to dynamic sql.
DECLARE #vReportData TABLE (
[persnr] int,
[date] datetime,
[abw] varchar(5),
[target] float,
[attendance] float
);
INSERT INTO #vReportData
([persnr], [date], [abw], [target], [attendance])
VALUES
(000001, '2020-01-01', '', 5.00, 4.45),
(000001, '2020-01-02', '', 0.00, 8.00),
(000001, '2020-01-04', '', 6.00, 7.00),
(000001, '2020-01-30', '', 6.00, 6.00),
(000001, '2020-01-31', '', 6.00, 6.50),
(999999, '2020-01-02', 'U', 6.00, 0.00),
(999999, '2020-01-30', '', 4.00, 5.00),
(999999, '2020-01-31', '', 5.00, 5.00)
declare #start datetime = '2020-01-01'
declare #ende datetime = '2020-01-04'
DECLARE #SubtotalPosition INT = DATEDIFF(DAY, #start,#ende) + 2
;with calender AS
(
select #start as Datum
union all
select Datum + 1 from Calender
where Datum + 1 <= #ende
)
,DistinctUsers AS
(
SELECT DISTINCT persnr FROM #vReportData
)
,MakeSubTotalPlaceholders AS
(
--Add placeholder to place at the end of the real data as subtotal buckets
select IsPlaceHolder=1, persnr, date = #ende, abw=null, target=0, attendance=null FROM DistinctUsers
UNION ALL
SELECT IsPlaceHolder=0, v.persnr, date, abw, target, attendance FROM #vReportData v
WHERE
v.date BETWEEN #start AND #ende
)
,ReadyForPivotWithTotal AS
(
SELECT
persnr, date, abw, target, attendance,
DayOffset = CASE WHEN IsPlaceHolder = 1 THEN #SubtotalPosition ELSE DENSE_RANK() OVER (ORDER BY DATEPART(DAY,date)) END,
total = CASE WHEN IsPlaceHolder = 1 THEN SUM(target) OVER (PARTITION BY persnr) ELSE target END
FROM
MakeSubTotalPlaceholders
)
SELECT persnr,
P1=MAX([1]),P2=MAX([2]),P3=MAX([3]),P4=MAX([4]),P5=MAX([5]),P6=MAX([6]),P7=MAX([7]),P8=MAX([8]),P9=MAX([9]),P10=MAX([10]),P11=MAX([11]),P12=MAX([12]),
P13=MAX([13]),P14=MAX([14]),P15=MAX([15]),P16=MAX([16]),P17=MAX([17]),P18=MAX([18]),P19=MAX([19]),P20=MAX([20]),P21=MAX([21]),P22=MAX([22]),P23=MAX([23]),P24=MAX([24]),
P25=MAX([25]),P26=MAX([26]),P27=MAX([27]),P28=MAX([28]),P29=MAX([29]),P30=MAX([30]),P31=MAX([31]),P32=MAX([32])
FROM
ReadyForPivotWithTotal A
PIVOT
(
MAX(total)
FOR DayOffSet IN
(
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],
[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],
[25],[26],[27],[28],[29],[30],[31],[32]
)
)AS B
GROUP BY
persnr

Yes, the pain with pivots is having to name all the columns in your build statement, especially when that list is big or prone to change -- which, as you've pointed out, is best done with dynamic SQL.
I've used the EOMONTH function, a recursive CTE and a temp table to populate the list of pivot columns. Then used dynamic SQL to build and execute the PIVOT.
I need a little more clarity around what the [sum] column is actually adding, and your expected output for your example data set, before I can incorporate that.
But without the [sum] column...
-- declare vars
DECLARE #columns_select AS VARCHAR(MAX), #columns_pivot AS VARCHAR(MAX) ;
DECLARE #start_date AS DATE, #end_date AS DATE ;
DECLARE #sqlCmd AS NVARCHAR(MAX) ;
-- determine start date
SET #start_date = ( SELECT TOP(1) DATEADD ( DD, 1, EOMONTH ( [date], -1 ) )
FROM #vReportData ORDER BY [date] ASC ) ;
-- determine end date
SET #end_date = ( SELECT TOP(1) EOMONTH ( [date], 0 )
FROM #vReportData ORDER BY [date] DESC ) ;
-- generate date range to temp table
WITH cte_DateCols1 AS
(
SELECT #start_date AS [date]
UNION ALL
SELECT DATEADD ( DD, 1, [date] )
FROM cte_DateCols1
WHERE [date] < #end_date
)
SELECT [date]
INTO #tbl_Dates
FROM cte_DateCols1 ;
-- populate list of columns for SELECT statement
SELECT #columns_select = CONCAT ( #columns_select, ', ISNULL ( ', QUOTENAME ( [date] ), ', '''' ) AS '
, QUOTENAME ( CONVERT ( VARCHAR(10), [date], 104 ) ) )
FROM #tbl_Dates ;
-- populate list of columns for PIVOT statement
SELECT #columns_pivot = CONCAT ( #columns_pivot, ', ', QUOTENAME ( [date] ) )
FROM #tbl_Dates ;
SET #columns_pivot = RIGHT ( #columns_pivot, LEN ( #columns_pivot ) - 2 ) ;
-- drop temp table
DROP TABLE #tbl_Dates ;
-- build dynamic SQL PIVOT statement
SET #sqlCmd = N'
WITH cte_Data AS
(
SELECT [persnr]
, CAST ( [date] AS DATE ) AS [date]
, CASE [abw]
WHEN ''U'' THEN ''U''
WHEN '''' THEN CONCAT ( [attendance], ''/'', [target] )
ELSE NULL
END AS [result]
FROM #vReportData
)
SELECT RIGHT ( ''00000'' + CAST ( [persnr] AS VARCHAR(6) ), 6 ) AS [persnr]
' + #columns_select + '
FROM cte_Data
PIVOT (
MAX ( [result] )
FOR [date]
IN ( ' + #columns_pivot + ' )
) AS pvt
ORDER BY [persnr] ASC ;' ;
-- execute dynamic SQL PIVOT statement
PRINT #sqlCmd ;
EXEC sp_executesql #statement = #sqlCmd ;
GO
Updated for Attendance Totals
You should be able to grab the [attendance] totals using a SUM function on the CTE output, before applying the pivot, and tack it onto the end of your pivot output, like this...
-- declare vars
DECLARE #columns_select AS VARCHAR(MAX), #columns_pivot AS VARCHAR(MAX) ;
DECLARE #start_date AS DATE, #end_date AS DATE ;
DECLARE #sqlCmd AS NVARCHAR(MAX) ;
-- determine start date
SET #start_date = ( SELECT TOP(1) DATEADD ( DD, 1, EOMONTH ( [date], -1 ) )
FROM #vReportData ORDER BY [date] ASC ) ;
-- determine end date
SET #end_date = ( SELECT TOP(1) EOMONTH ( [date], 0 )
FROM #vReportData ORDER BY [date] DESC ) ;
-- generate date range to temp table
WITH cte_DateCols AS
(
SELECT #start_date AS [date]
UNION ALL
SELECT DATEADD ( DD, 1, [date] )
FROM cte_DateCols
WHERE [date] < #end_date
)
SELECT [date]
INTO #tbl_Dates
FROM cte_DateCols ;
-- populate list of columns for SELECT statement
SELECT #columns_select = CONCAT ( #columns_select, ', ISNULL ( ', QUOTENAME ( [date] ), ', '''' ) AS '
, QUOTENAME ( CONVERT ( VARCHAR(10), [date], 104 ) ) )
FROM #tbl_Dates ;
-- populate list of columns for PIVOT statement
SELECT #columns_pivot = CONCAT ( #columns_pivot, ', ', QUOTENAME ( [date] ) )
FROM #tbl_Dates ;
SET #columns_pivot = RIGHT ( #columns_pivot, LEN ( #columns_pivot ) - 2 ) ;
-- drop temp table
DROP TABLE #tbl_Dates ;
-- build dynamic SQL PIVOT statement
SET #sqlCmd = N'
WITH cte_Data AS
(
SELECT [persnr]
, CAST ( [date] AS DATE ) AS [date]
, CASE [abw]
WHEN ''U'' THEN ''U''
WHEN '''' THEN CONCAT ( [attendance], ''/'', [target] )
ELSE NULL
END AS [result]
FROM #vReportData
)
, cte_DataWithTotals AS
(
SELECT r.[persnr]
, SUM ( CAST ( r.[attendance] AS DECIMAL (5,2) ) ) AS [total_attendance]
, d.[date]
, d.[result]
FROM #vReportData AS r
INNER JOIN cte_Data AS d
ON r.[persnr] = d.[persnr]
GROUP BY r.[persnr], d.[date], d.[result]
)
SELECT RIGHT ( ''00000'' + CAST ( [persnr] AS VARCHAR(6) ), 6 ) AS [persnr]
' + #columns_select + '
, [total_attendance] AS [attendance_sum]
FROM cte_DataWithTotals
PIVOT (
MAX ( [result] )
FOR [date]
IN ( ' + #columns_pivot + ' )
) AS pvt
ORDER BY [persnr] ASC ;' ;
-- execute dynamic SQL PIVOT statement
PRINT #sqlCmd ;
EXEC sp_executesql #statement = #sqlCmd ;
GO

Related

SQL Pivot and Group By By Date and Totals

I am using SQL Server 13.0 Developer Edition.
I can't make the correct structure for the SQL code with Pivot and Group by clauses.
I have data like;
Id
OperationType
Date
ResultCode
1
BeginTransaction
2022-12-01 16:54:30
-28
2
BeginTransaction
2022-12-02 18:54:30
-30
3
BeginTransaction
2022-12-02 18:54:30
-30
4
BeginTransaction
2022-12-03 14:54:30
-10
5
BeginTransaction
2022-12-03 11:54:30
-5
6
BeginTransaction
2022-12-05 10:54:30
-3
and I want to see total number of ResultCodes per day but I want to generate ResultCode columns dynamicly because I have so much different result codes. Query result should be like;
Day
-3
-5
-10
-28
-30
Total
2022-12-01
0
0
0
1
0
1
2022-12-02
0
0
0
0
2
2
2022-12-03
0
1
1
0
0
2
2022-12-05
1
0
0
0
0
1
I wrote this query but it says The incorrect value "ResultCode" is supplied in the PIVOT operator.
Select * from (SELECT CAST(Date as date),
COUNT(ResultCode) as Result,
COUNT(*) AS Totals
FROM OperationLogs
WHERE OperationType = 'Begin'
GROUP BY CAST(StartTime as date)
) As Source
PIVOT (
COUNT(Result) FOR Result IN ([ResultCode])
) AS PivotTable
ORDER BY ForDate
Can anyone help me with how can I group by date and also have counts for ResultCodes as colums and a Total by day?
CREATE TABLE #ResultCodes (
Id INT,
OperationType VARCHAR(50),
[Date] DateTime,
ResultCode INT
)
INSERT INTO #ResultCodes(Id,OperationType,[Date],ResultCode) VALUES
(1,'BeginTransaction','2022-12-01 16:54:30',-28),
(2,'BeginTransaction','2022-12-02 18:54:30',-30),
(3,'BeginTransaction','2022-12-02 18:54:30',-30),
(4,'BeginTransaction','2022-12-03 14:54:30',-10),
(5,'BeginTransaction','2022-12-03 11:54:30',-5),
(6,'BeginTransaction','2022-12-05 10:54:30',-3)
DECLARE #COLUMNS AS NVARCHAR(MAX)
DECLARE #QUERY AS NVARCHAR(MAX)
SET #COLUMNS = STUFF((SELECT ',' + QUOTENAME(ResultCode)
FROM #ResultCodes GROUP BY ResultCode ORDER BY ResultCode DESC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #QUERY = N'
SELECT [Date],'+#COLUMNS+', tc AS "Total"
FROM (
SELECT
[tc] = COUNT(CAST([Date] AS date)) over(partition by CAST([Date] AS date)),
CAST([Date] AS date) AS "Date",
[ResultCode]
FROM #ResultCodes
) AS tb
PIVOT (
COUNT([ResultCode])
FOR [ResultCode]
IN (
'+#COLUMNS+'
)
) AS P';
EXEC(#QUERY)
DROP TABLE IF EXISTS #ResultCodes
You can find the answer here : https://www.sqlshack.com/dynamic-pivot-tables-in-sql-server/
and below
USE [tempdb]
IF OBJECT_ID ('[Date]') IS NOT NULL DROP TABLE [Date]
CREATE TABLE [Date] ([Id] int , [OperationType] varchar(50) , [Date] datetime , [ResultCode] int)
INSERT INTO [Date] VALUES (1, 'BeginTransaction', '2022-12-01 16:54:30', -28) , (2, 'BeginTransaction', '2022-12-02 18:54:30', -30) , (3, 'BeginTransaction', '2022-12-02 18:54:30', -30) , (4, 'BeginTransaction', '2022-12-03 14:54:30', -10) , (5, 'BeginTransaction', '2022-12-03 11:54:30', -5) , (6, 'BeginTransaction', '2022-12-05 10:54:30', -3)
DECLARE #pivotcolumns varchar(500) = STUFF((SELECT ',' + QUOTENAME(CAST([ResultCode] AS varchar(20))) FROM [Date] GROUP BY [ResultCode] ORDER BY [ResultCode] FOR XML PATH('')),1,1,'')
DECLARE #SqlStatement NVARCHAR(MAX)
SET #SqlStatement = N'
SELECT * , SUM('+REPLACE(#pivotcolumns, ',', '+')+') OVER (PARTITION BY CAST([Date] AS date)) AS "Total" FROM (
SELECT
CAST([Date] AS date) AS "Date", [ResultCode]
FROM [Date]
) AS t1
PIVOT (
COUNT([ResultCode])
FOR [ResultCode]
IN (
'+#pivotcolumns+'
)
) AS PivotTable
';
EXEC(#SqlStatement)

SQL Server 2016: Turn multiple lines into columns with the different iterations

I have a table that has many InsuranceNo's for unique MemberIDs. If there are more than one InsuranceNo, I want the InsuranceNo's to shift to a column, so in the end there is one line per MemberID, with all the iterations of that ID's InsuranceNo's as a Column.
MemberID InsuranceNo
--------------------------
123456 dser
124571 jklh
123456 abcd
I want it to look like this:
MemberID InsuranceNo1 InsuranceNo2
-----------------------------------------------------
123456 dser abcd
124571 jklh
Thank you!
Yet another option... Just change "YourTable" to your actual table name.
Example
Declare #SQL varchar(max) = '
Select *
From (
Select MemberID
,Item = concat(''InsuranceNo'',row_number() over (Partition By MemberID Order By (Select NULL)))
,Value = InsuranceNo
From YourTable
) A
Pivot (max([Value]) For [Item] in (' + Stuff((Select ','+QuoteName(concat('InsuranceNo',ColNr))
From (Select Distinct ColNr=row_number() over (Partition By MemberID Order By (Select NULL)) from YourTable ) A
For XML Path('')),1,1,'') + ') ) p'
--Print #SQL
Exec(#SQL);
Returns
MemberID InsuranceNo1 InsuranceNo2
123456 dser abcd
124571 jklh NULL
If it helps wrap your head around PIVOT, the SQL Generated looks like this:
Select *
From (
Select MemberID
,Item = concat('InsuranceNo',row_number() over (Partition By MemberID Order By (Select NULL)))
,Value = InsuranceNo
From YourTable
) A
Pivot (max([Value]) For [Item] in ([InsuranceNo1],[InsuranceNo2]) ) p
I prefer a dynamic cross tab to the dynamic pivot. I find the syntax far less obtuse and it is super easy if you need to add additional columns. Here is I would go about tackling this. Of course in your case you don't need a temp table because you have an actual table to use.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
MemberID int
, InsuranceNo varchar(10)
)
insert #Something values
(123456, 'dser')
, (124571, 'jklh')
, (123456, 'abcd')
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by MemberID order by InsuranceNo) as RowNum
from #Something
)
select MemberID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by MemberID order by MemberID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then InsuranceNo end) as InsuranceNo' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from #Something
group by MemberID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
exec sp_executesql #SqlToExecute

How to get row data in columns and column data in rows using pivot in SQL Server 2008 R2

Current result which I am getting from my stored procedure:
Expected result from current result:
An image only answer to an image only question!
Try this..
Union all with Pivot..
Demo Here
declare #table table
(period nvarchar(10),num int,cost int,value int)
insert into #table
select 'Jan-16',1,100,100
union all select 'Feb-16',2,200,200
union all select 'Mar-16',3,300,300
union all select 'Apr-16',4,400,400
union all select 'May-16',5,500,500
union all select 'Jun-16',6,600,600
union all select 'Jul-16',7,700,700
union all select 'Aug-16',8,800,800
union all select 'Sep-16',9,900,900
union all select 'Oct-16',10,1000,1000
union all select 'Nov-16',11,1100,1100
union all select 'Dec-16',12,1200,1200
--select * from #table
select 'No of Order' as Period,[Jan-16], [Feb-16],[Mar-16],[Apr-16],[May-16],[Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16],[Dec-16]
from
(
select period,num from #table) u
pivot ( sum(num) for period in ([Jan-16] ,[Feb-16],[Mar-16],[Apr-16], [May-16], [Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16], [Dec-16]))
as pvt
union all
select 'Cost Of Order' as Period,[Jan-16], [Feb-16],[Mar-16],[Apr-16],[May-16],[Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16],[Dec-16]
from
(
select period,cost from #table) u
pivot ( sum(Cost) for period in ([Jan-16] ,[Feb-16],[Mar-16],[Apr-16], [May-16], [Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16], [Dec-16]))
as pvt
union all
select 'Value Of Order' as Period,[Jan-16], [Feb-16],[Mar-16],[Apr-16],[May-16],[Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16],[Dec-16]
from
(
select period,value from #table) u
pivot ( sum(value) for period in ([Jan-16] ,[Feb-16],[Mar-16],[Apr-16], [May-16], [Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16], [Dec-16]))
as pvt
Select DaysToManufacture, AVG(StandardCost) As AverageCost
From Production.Product
Group by DaysToManufacture
Here is the result set.
DaysToManufacture AverageCost
0 5.0885
1 223.88
2 359.1082
4 949.4105
No products are defined with three DaysToManufacture.
The following code displays the same result, pivoted so that the DaysToManufacture values become the column headings. A column is provided for three [3] days, even though the results are NULL.
-- Pivot table with one row and five columns
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days,
[0], [1], [2], [3], [4]
FROM
(SELECT DaysToManufacture, StandardCost
FROM Production.Product) AS SourceTable
PIVOT
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
) AS PivotTable;
Here is the result set.
Cost_Sorted_By_Production_Days 0 1 2 3 4
AverageCost 5.0885 223.88 359.1082 NULL 949.4105
My Example:
DECLARE
#v_Columns VARCHAR(MAX),
#v_StartDate DATETIME = '09/15/2015',
#v_EndDate DATETIME = '12/15/2020',
#v_Query VARCHAR(MAX)
SELECT #v_Columns = COALESCE(#v_Columns,'[') + convert(varchar, Date, 101) + '],['
FROM
(SELECT DISTINCT Date FROM view_wc_sessions_info) th
WHERE
th.Date BETWEEN #v_StartDate AND #v_EndDate
SET #v_Columns = SUBSTRING(#v_Columns, 1, LEN(#v_Columns)-2)
SET #v_Query =
'SELECT
*
FROM
(
select [Total], Date, fk_student_rcid
from view_WC_SESSIONS_Info th
WHERE
th.Date BETWEEN ''' + CONVERT(VARCHAR(50), #v_StartDate, 101) + '''
AND ''' + CONVERT(VARCHAR(50), #v_EndDate, 101) + '''
) src
PIVOT
(
COUNT(src.[Total])
FOR src.Date IN (' + #v_Columns + ')
) AS pivotview'
EXEC(#v_Query)

2005 SSRS/SQL Server PIVOT results need reversing

Preamble: I've read through the three questions/answers here,here, and here, with big ups to #cade-roux. This all stemmed from trying to use the following data in a 2005 SSRS matrix that, I believe, doesn't work because I want to show a member having to take a test multiple times, and SSRS seems to require the aggregate where I want to show all dates.
I get the following results in my table, which seems to be showing all the data correctly:
How do I change the code below to show a) the "tests" at the top of each column with b) if it's called for, the multiple dates that test was taken?
Here's the code I have to produce the table, above. Much of it is commented out as I was just trying to get the pivot to work, but you may notice I am also trying to specify which test column comes first.
CREATE TABLE #tmp ( ---THIS WORKS BUT TESTS ARE VERTICAL
[TEST] [varchar](30) NOT NULL,
[ED] [datetime] NOT NULL
)
--WHERE THE TEST AND ED COME FROM
INSERT #TMP
SELECT DISTINCT
-- N.FULL_NAME
-- , CONVERT(VARCHAR(30), AM.CREATEDATE, 101) AS ACCOUNT_CLAIMED
-- , N.EMAIL
-- , NULL AS 'BAD EMAIL'
-- , CONVERT(VARCHAR(30), AC.EFFECTIVE_DATE, 101) AS EFFECTIVE_DATE
AC.PRODUCT_CODE AS TEST
, CONVERT(VARCHAR(30), AC.EFFECTIVE_DATE, 101) AS ED
-- , CASE
-- WHEN AC.PRODUCT_CODE = 'NewMem_Test' THEN '9'
-- WHEN AC.PRODUCT_CODE = 'NM_Course1' THEN '1'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course1' THEN '2'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course2' THEN '3'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course3' THEN '4'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course4' THEN '5'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course5' THEN '6'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course6' THEN '7'
-- WHEN AC.PRODUCT_CODE = 'NMEP_Course7' THEN '8'
-- END AS 'COLUMN_ORDER'
FROM NAME N
JOIN USERMAIN UM
ON N.ID = UM.CONTACTMASTER
JOIN formTransLog TL
ON UM.USERID = TL.USERNAME
JOIN anet_Users AU
ON UM.USERID = AU.USERNAME
JOIN anet_Membership AM
ON AU.USERID = AM.USERID
JOIN ACTIVITY AC
ON N.ID = AC.ID
AND AC.ACTIVITY_TYPE = 'COURSE'
AND AC.PRODUCT_CODE LIKE 'N%'
--ORDER BY 1, 7
DECLARE #sql AS varchar(max)
DECLARE #pivot_list AS varchar(max) -- Leave NULL for COALESCE technique
DECLARE #select_list AS varchar(max) -- Leave NULL for COALESCE technique
SELECT #pivot_list = COALESCE(#pivot_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + ']'
,#select_list = COALESCE(#select_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + '] AS [col_' + CONVERT(varchar, PIVOT_CODE) + ']'
FROM (
SELECT DISTINCT PIVOT_CODE
FROM (
SELECT TEST, ED, ROW_NUMBER() OVER (PARTITION BY TEST ORDER BY ED) AS PIVOT_CODE
FROM #tmp
) AS rows
) AS PIVOT_CODES
SET #sql = '
;WITH p AS (
SELECT TEST, ED, ROW_NUMBER() OVER (PARTITION BY TEST ORDER BY ED) AS PIVOT_CODE
FROM #tmp
)
SELECT TEST, ' + #select_list + '
FROM p
PIVOT (
MIN(ED)
FOR PIVOT_CODE IN (
' + #pivot_list + '
)
) AS pvt
'
PRINT #sql
EXEC (#sql)
EDIT:
The goal is to have the report in SSRS look like this:
I was able to produce the results you were looking for by adding in a number (RowNum) to the query underneath the PIVOT operator. It doesn't have to be in the final query (though you might want it for client-side sorting), but by having it in the underlying layer the PIVOT operation treats that number like a member of a GROUP BY clause.
Please look through my sample SQL below and let me know if this matches your criteria.
CREATE TABLE #TMP
(
Name VARCHAR(10),
Test VARCHAR(20),
EffectiveDate DATETIME
)
INSERT INTO #TMP (Name, Test, EffectiveDate)
SELECT 'Jane', 'NM_Course1', '01/17/2014' UNION
SELECT 'Jane', 'NMEP_Course1', '12/19/2013' UNION
SELECT 'Jane', 'NMEP_Course1', '12/20/2013' UNION
SELECT 'Jane', 'NMEP_Course2', '12/19/2013' UNION
SELECT 'Jane', 'NMEP_Course2', '12/22/2013' UNION
SELECT 'Jane', 'NMEP_Course2', '01/05/2014' UNION
SELECT 'John', 'NM_Course1', '01/17/2014' UNION
SELECT 'John', 'NMEP_Course1', '01/11/2014'
DECLARE #sql AS varchar(max)
DECLARE #pivot_list AS varchar(max) -- Leave NULL for COALESCE technique
DECLARE #select_list AS varchar(max) -- Leave NULL for COALESCE technique
SELECT #pivot_list = COALESCE(#pivot_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + ']'
,#select_list = COALESCE(#select_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + '] AS [col_' + CONVERT(varchar, PIVOT_CODE) + ']'
FROM (
SELECT DISTINCT PIVOT_CODE
FROM (
SELECT TEST AS PIVOT_CODE
FROM #tmp
) AS rows
) AS PIVOT_CODES
SET #sql = '
SELECT Name, ' + #select_list + '
FROM
(
SELECT b.Name, RowNum, b.EffectiveDate, b.TEST AS PIVOT_CODE
FROM
(
SELECT Name, Test, EffectiveDate, ROW_NUMBER() OVER (PARTITION BY NAME, TEST ORDER BY EffectiveDate) RowNum
FROM #Tmp
) b
) p
PIVOT (
MIN(EffectiveDate)
FOR PIVOT_CODE IN (
' + #pivot_list + '
)
) AS pvt
ORDER BY Name, RowNum
'
PRINT #sql
EXEC (#sql)
DROP TABLE #TMP

Dynamic SQL in Stored Procedure - Datetime parameters

Got a Stored Procedure that has is being converted to Dynamic SQL, the reason is because additional SQL will be passed into the procedure from an external system, before it is executed.
Conversion failed when converting datetime from character string.
Here is the full Stored Procedure:
USE [DBName];
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
ALTER PROCEDURE [DB_Admin].[GetMiniCalendarDataNew]
#userID int, #startDate datetime, #endDate datetime, #JVID int = 0
WITH EXEC AS CALLER
AS
set nocount on
declare #SQLQuery AS NVARCHAR(max)
declare #t as table([day] int, [end] datetime, sortorder int, jv int)
SET #SQLQuery= 'insert into #t([day], [end], sortorder, jv)
select day((A.STARTTIME)) [day], max(a.endtime) ''end'', 3 sortorder,min(a.jv) jv
from DB_Admin.CSTM_CALENDAR a
join DB_Admin.CSTM_CALENDAR b on a.id<>b.id
join DB_Admin.CSTM_CALENDARParticipants m1 on a.id=m1.CalendarID
join DB_Admin.CSTM_CALENDARParticipants m2 on b.id=m2.CalendarID
join DB_Admin.DTree DTree on a.FolderDataID=DTree.DataID
where a.starttime between ' + CAST(#startDate AS DATETIME) + ' AND ' + CAST(#endDate AS DATETIME) +
' AND DTree.OwnerID > 0
and b.starttime between ' + CAST(#startDate AS DATETIME) + ' AND ' + CAST(#endDate AS DATETIME) +
' AND a.starttime<b.endtime --find overlapping meetings
AND a.endtime>b.starttime --find overlapping meetings
AND M1.PARTICIPANT IN (
select id from DB_Admin.kuaf where id in (
select id from DB_Admin.kuafchildren
where childid=' +#userID+')
or id=' +#userID+
')
AND M2.PARTICIPANT IN (
select id from DB_Admin.kuaf where id in (
select id from DB_Admin.kuafchildren
where childid='+#userID+')
or id='+#userID+
')'+
--Filter on JV
' AND ( exists (select 1 where a.jv='+#JVID+')
or '+#JVID+'=0'+
')'+
'group by day(A.STARTTIME)'
+' insert into #t ([day], [end], sortorder, jv)
select day(A.STARTTIME) [day], max(a.endtime) ''end'', 2 SORTORDER,min(a.jv) jv
from DB_Admin.CSTM_CALENDAR a
join DB_Admin.CSTM_CALENDAR b on a.id<>b.id
join DB_Admin.CSTM_CALENDARParticipants m1 on a.id=m1.CalendarID
join DB_Admin.CSTM_CALENDARParticipants m2 on b.id=m2.CalendarID
join DB_Admin.DTree DTree on a.FolderDataID=DTree.DataID
where a.starttime between ' + CAST(#startDate AS DATETIME) +' AND ' +CAST(#endDate AS DATETIME)+
' AND DTree.OwnerID > 0
--Filter on JV
AND ( exists (select 1 where a.jv='+#JVID+')
or '+#JVID+'=0'+')
and M1.PARTICIPANT IN (
select id from DB_Admin.kuaf where id in (
select id from DB_Admin.kuafchildren
where childid='+#userID+')
or id='+#userID+
')
group by (A.STARTTIME)'+
' insert into #t ([day], [end], sortorder, jv)
select day(A.STARTTIME) [day], max(a.endtime) ''end'', 1 SORTORDER,min(a.jv) jv
from DB_Admin.CSTM_CALENDAR a
join DB_Admin.CSTM_CALENDARParticipants m1 on a.ID=m1.CalendarID
join DB_Admin.DTree DTree on a.FolderDataID=DTree.DataID
where a.starttime between '+CAST(#startDate AS DATETIME)+' AND '+CAST(#endDate AS DATETIME)+
' AND DTree.OwnerID > 0
--Filter on JV
AND ( exists (select 1 where a.jv='+#JVID+')
or '+#JVID+'=0'+ ')
and M1.PARTICIPANT NOT IN (
select id from DB_Admin.kuaf where id in (
select id from DB_Admin.kuafchildren
where childid='+#userID+')
or id='+#userID+'
)
group by (A.STARTTIME)'
--format query
+' select [day], max(month('+CAST(#startDate AS DATETIME)+' [month], max(year('+CAST(#endDate AS DATETIME)+')) [year], max([end]) ''end'',
case
when max(sortorder)=3 then ''Overlapping''
when max(sortorder)=2 then ''Participating''
when max(sortorder)=1 then ''Existing''
when max(sortorder)=0 then ''Empty''
end sortOrder , min(jv) JVID
from #t
group by [day]
order by sortorder desc'
--EXEC (#SQLQuery)
PRINT (#SQLQuery)
GO
Well, you need to
Quoting them
Ensure they are string
Make them language/locale safe
So:
...
where a.starttime between ''' + CONVERT(varchar(30), #startDate, 126) +''' AND ''' + ...
...
Edit:
int error. You need to CAST #userID to varchar concatenate it.
SQL doesn't do VBA style implicit CASTs
I think your problem is here:
'where a.starttime between ' + CAST(#startDate AS DATETIME) + ' AND ' + CAST(#endDate AS DATETIME) +
You should be converting it to a string, rather than a datetime. See http://msdn.microsoft.com/en-us/library/ms187928.aspx