Dynamic SQL in Stored Procedure - Datetime parameters - sql

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

Related

Pivot with dates of given month

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

SQL query to get customers yearly net amount and Year to Year growth percentage

Here is the scenario,
I am creating a report where, for each customer yearly ordered amount will be shown. And than the growth percentage comparing the previous years.
What I have tried so far :
SELECT * FROM (select MrCode MrCode17,MrName MrName17, SUM(NetAmt) NetAmount17,SUM(TotalNetAmt) TotalNetAmount17 from TASKORDER_REGISTER where UCODE = 'SR01'
AND TaskOrder_Dt between '2017-1-1 00:00:00' and '2017-12-31 00:00:00'
GROUP BY MrCode,MrName) as abc
FULL JOIN
(select MrCode MrCode16,MrName MrName16, SUM(NetAmt) NetAmount16,SUM(TotalNetAmt) TotalNetAmount16 from TASKORDER_REGISTER where UCODE = 'SR01'
AND TaskOrder_Dt between '2016-1-1 00:00:00' and '2016-12-31 00:00:00'
GROUP BY MrCode,MrName) AS bbb
ON abc.MrCode17 = bbb.MrCode16
FULL JOIN
(select MrCode MrCode15,MrName MrName15, SUM(NetAmt) NetAmount15,SUM(TotalNetAmt) TotalNetAmount15 from TASKORDER_REGISTER where UCODE = 'SR01'
AND TaskOrder_Dt between '2015-1-1 00:00:00' and '2015-12-31 00:00:00'
GROUP BY MrCode,MrName) AS ccc
ON bbb.MrCode16 = ccc.MrCode15
What is the result :
I am not sure what is the best procedure to accmplish the task. There must sume better queries to get data. And I could not create the growth percentage yet.
Please use following query it will return Year,Difference and [Difference Precantage].
Please add other columns in result set
IF OBJECT_ID('tempdb..#SalesData') IS NOT NULL BEGIN DROP TABLE #SalesData END
IF OBJECT_ID('tempdb..#FinalData') IS NOT NULL BEGIN DROP TABLE #FinalData END
SELECT IDENTITY(int, 1,1) AS ID_Num ,year(TaskOrder_Dt) SalesYear,SUM(NetAmt) TotalAmount
INTO #SalesData
FROM TASKORDER_REGISTER GROUP BY year(TaskOrder_Dt)
SELECT
[current].ID_Num,
[current].TotalAmount,
[current].SalesYear,
[current].TotalAmount - ISNULL([next].TotalAmount, 0) AS Diff,
(([current].TotalAmount - ISNULL([next].TotalAmount,0)) * ISNULL([next].TotalAmount,1)) * 100 AS Diffper
INTO #FinalData
FROM #SalesData AS [current]
LEFT JOIN #SalesData AS [next]
ON [next].ID_Num = (SELECT MAX(ID_Num) FROM #SalesData WHERE ID_Num < [current].ID_Num)
DECLARE #cols AS NVARCHAR(MAX),#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(SalesYear) FROM #FinalData FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'),1,1,'')
SELECT #query = 'SELECT * FROM (SELECT [SalesYear],Diff,Diffper FROM #FinalData) X
PIVOT (AVG(Diff) for [SalesYear] in (' + #cols + ')) P'
EXEC SP_EXECUTESQL #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

SQL Dynamic Pivot

I have written a dynamic SQL Pivot Query that returns a Quantity per day, the number of columns returned are dynamic based on the number of days between the opening parameters. The issue I am having is that the Columns are not in DateOrder, I am guessing I need some form or Order By to fix this, does anyone have any ideas where I would need to put it in?
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
--declare and set variables
DECLARE #v_Columns VARCHAR(MAX)
,#v_StartDate DATETIME = GETDATE() - 10
,#v_EndDate DATETIME = GETDATE()
,#v_Query VARCHAR(max)
--select results
SELECT #v_Columns = Coalesce(#v_Columns, '[') + convert(VARCHAR, cast(DateCreated AS DATE), 111) + '],['
FROM (
SELECT DISTINCT cast(DateCreated AS DATE) AS DateCreated
FROM TransactionOriginTracking
) tot
WHERE tot.DateCreated BETWEEN #v_StartDate
AND #v_EndDate
SET #v_Columns = SUBSTRING(#v_Columns, 1, Len(#v_Columns) - 2)
SET #v_Query = 'select
*
from
(
SELECT
cast(Tracking.DateCreated as date) as [Date],
th.TotalQuantity [Quantity]
FROM
TransactionHeader th
Inner JOin TransactionOriginTracking as Tracking on Tracking.TransactionHeaderId = th.Id
and Tracking.WorkflowStageId = 9
Inner Join CompanyDivision on CompanyDivision.Id = th.CompanyDivisionId
WHERE
Tracking.DateCreated between ''' + CONVERT(VARCHAR(50), #v_StartDate, 111) + ''' AND ''' + CONVERT(VARCHAR(50), #v_EndDate, 111) + '''
) as src
Pivot
(
SUM(src.Quantity)
For src.Date IN (' + #v_Columns + ')
) as PivotView'
EXEC (#v_Query)
When you build up your column list, sort by date:
--select results
SELECT
#v_Columns = Coalesce(#v_Columns, '[') + convert(VARCHAR, cast(DateCreated AS DATE), 111) + '],['
FROM
(SELECT DISTINCT
CAST(DateCreated AS DATE) AS DateCreated
FROM
TransactionOriginTracking) tot
WHERE
tot.DateCreated BETWEEN #v_StartDate AND #v_EndDate
ORDER BY
tot.DateCreated

sql query including month columns?

I was wondering if I could get some ideas or direction on a sql query that would output column months. Here is my current query..
select A.assetid, A.Acquisition_Cost, B.modepreciaterate from FA00100 A
inner join FA00200 B on A.assetindex = B.assetindex
where MoDepreciateRate != '0'
I would like to add more columns that look as such:
select assetid, acquisition_cost, perdeprrate, Dec_2012, Jan_2013, Feb_2013....
where Dec_2012 = (acquisition_cost - MoDepreciateRate*(# of months))
and Jan_2013 = (acquisition_cost - MoDepreciateRate*(# of months))
where # of months can be changed.
Any help would be really appreciated. Thank you!
Here is an example of what I would like the output to be with '# of months' = 4
assetid SHRTNAME Acquisition_Cost perdeprrate Dec_2012 Jan_2013 Feb_2013 Mar_2013
CS-013 GEH INTEG 17490.14 485.83 17004.31 16518.48 16032.65 15546.82
CS-014 WEB BRD 14560 404.4507 14155.5493 13751.0986 13346.6479 12942.1972
Try This:
--setup
create table #fa00100 (assetId int, assetindex int, acquisitionCost int, dateAcquired date)
create table #fa00200 (assetIndex int, moDepreciateRate int, fullyDeprFlag nchar(1), fullyDeprFlagBit bit)
insert #fa00100
select 1, 1, 100, '2012-01-09'
union select 2, 2, 500, '2012-05-09'
insert #fa00200
select 1, 10, 'N', 0
union select 2, 15, 'Y', 1
.
--solution
create table #dates (d date not null primary key clustered)
declare #sql nvarchar(max)
, #pivotCols nvarchar(max)
, #thisMonth date
, #noMonths int = 4
set #thisMonth = cast(1 + GETUTCDATE() - DAY(getutcdate()) as date)
select #thisMonth
while #noMonths > 0
begin
insert #dates select DATEADD(month,#noMonths,#thisMonth)
set #noMonths = #noMonths - 1
end
select #sql = ISNULL(#sql + NCHAR(10) + ',', '')
--+ ' A.acquisitionCost - (B.moDepreciateRate * DATEDIFF(month,dateAcquired,''' + convert(nvarchar(8), d, 112) + ''')) ' --Original Line
+ ' case when A.acquisitionCost - (B.moDepreciateRate * DATEDIFF(month,dateAcquired,''' + convert(nvarchar(8), d, 112) + ''')) <= 0 then 0 else A.acquisitionCost - (B.moDepreciateRate * DATEDIFF(month,dateAcquired,''' + convert(nvarchar(8), d, 112) + ''')) end ' --new version
+ quotename(DATENAME(month, d) + '_' + right(cast(10000 + YEAR(d) as nvarchar(5)),4))
from #dates
set #sql = 'select A.assetid
, A.acquisitionCost
, B.moDepreciateRate
,' + #sql + '
from #fa00100 A
inner join #fa00200 B
on A.assetindex = B.assetindex
where B.fullyDeprFlag = ''N''
and B.fullyDeprFlagBit = 0
'
--nb: B.fullyDeprFlag = ''N'' has double quotes to avoid the quotes from terminating the string
--I've also included fullyDeprFlagBit to show how the SQL would look if you had a bit column - that will perform much better and will save space over using a character column
print #sql
exec(#sql)
drop table #dates
.
--remove temp tables from setup
drop table #fa00100
drop table #fa00200