Dynamic Multi Pivot in SQL Server - sql

I am new to SQL Server Pivot. I have an input table with data represented like the below. It has model data with three amount columns. The amounts are applicable for those models based on the date provided.
I am trying to generate a report like the below in a dynamic fashion where distinct number of dates along with three amounts should be displayed in the report for each model.
I have tried a Dynamic SQL like the below.
SELECT #pivotcols = STUFF((
SELECT ',' + QUOTENAME(DATE)
FROM #table
GROUP BY Date
ORDER BY Date
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = N'SELECT Model,' + #pivotcols + N' from
(
SELECT Model
,Amount1
,Amount2
,Amount3
,[DATE] AS DATE1
,[DATE] AS DATE2
,[DATE] AS DATE3
FROM #table
) x
pivot
(
max(amount1)
for Date1 in (' + #pivotcols + N')
) r
pivot
(
max(amount2)
for Date2 in (' + #pivotcols + N')
) p
pivot
(
max(amount3)
for Date3 in (' + #pivotcols + N')
) o '
EXEC sp_executesql #query;
When I try with this query I am getting the following error.
The column name "2000-01-01" specified in the PIVOT operator conflicts with the existing column name in the PIVOT argument.
Kindly show some light on this.

Finally I achieved the results using the below query
DECLARE #selectcols AS NVARCHAR(MAX) = ''
,#pivotcols AS NVARCHAR(MAX) = ''
,#query AS NVARCHAR(MAX) = ''
SELECT #selectcols = STUFF((
SELECT ',[AMT1-' + convert(VARCHAR(10), [DATE], 101) + ']'
+ ',[AMT2-' + convert(VARCHAR(10), [DATE], 101) + ']'
+ ',[AMT3-' + convert(VARCHAR(10), [DATE], 101) + ']'
+ ',[TOT-' + convert(VARCHAR(10), [DATE], 101) + ']'
FROM #table
GROUP BY [DATE]
ORDER BY [DATE]
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT #pivotcols = STUFF((
SELECT ',[AMT1-' + convert(VARCHAR(10), [DATE], 101) + ']'
FROM #table
GROUP BY [DATE]
ORDER BY [DATE]
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = N'SELECT [Model],' + #selectcols + N' from
(
select [Model],AMT1,AMT2,AMT3,AMT1+AMT2+AMT3 AS TOT
,''AMT1-'' + convert(VARCHAR(10), [DATE], 101) as DATE1
,''AMT2-'' + convert(VARCHAR(10), [DATE], 101) as DATE2
,''AMT3-'' + convert(VARCHAR(10), [DATE], 101) as DATE3
,''TOT-'' + convert(VARCHAR(10), [DATE], 101) as DATE4
from #table
) x
pivot
(
max(AMT1)
for DATE1 in (' + #pivotcols + N')
) r
pivot
(
max(AMT2)
for DATE2 in (' + replace(#pivotcols,'AMT1','AMT2') + N')
) p
pivot
(
max(AMT3)
for DATE3 in (' + replace(#pivotcols,'AMT1','AMT3') + N')
) o
pivot
(
max(TOT)
for DATE4 in (' + replace(#pivotcols,'AMT1','TOT') + N')
) t'
EXEC sp_executesql #query;

Related

how to set space between two pivot column

query pivot with two word with space in column
DECLARE #cols NVARCHAR(MAX) SELECT
top 24 #cols = COALESCE(#cols + ',[' + CONVERT(varchar, DateIn ,21 ) + ']','[' + CONVERT(varchar, DateIn ,21) + ']' )
FROM DailyAttendanceMaster where DateIn between '2018-10-01 00:00:00.000' and '2018-10-24 00:00:00.000'
DECLARE #qry NVARCHAR(4000) SET
#qry = 'SELECT * FROM (SELECT Employee_Master.Employee_Name, convert(char(5), TimeIn, 108)+ + convert(char(5), TimeOutD, 108)TimeIn , DailyAttendanceMaster.DateIn FROM DailyAttendanceMaster inner join Employee_Master on Employee_Master.essl_EmpID=DailyAttendanceMaster.EMPID)emp
PIVOT (MAX(TimeIn) FOR DateIn IN (' + #cols + ')) AS stat'
EXEC(#qry)
here is my pivot query i am concatenate two column that is TimeIn and TimeOutD it work correct but i want sapce between two column so how can i do it
enter image description here

SQL - Pivoting on Column and Row Values

I'm trying to Pivot a Table on X and Y position. The table is in a format similar to below.
Each row has a value which is relative to its Row and Column Position.'AThing' and 'FileName' are to be ignored in the data set.
So if this was pivoted we would get:
Iv'e been trying for a while but can't seem to figure it out, any ideas?
EDIT: Number of Fields are dynamic per 'FileName'. I have managed to extract the column names but not the data using:
-- Construct List of Columns to Pivot
SELECT #PivotCols =
STUFF(
(SELECT ',' + QUOTENAME(FieldName)
FROM #Data
GROUP BY ColPos, FieldName
ORDER BY ColPos ASC
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #PivotQuery =
SELECT ' + #PivotCols + N'
FROM
(
SELECT ColPos, FieldName
FROM #Data
GROUP BY ColPos, FieldName
) x
PIVOT
(
MIN(ColPos)
FOR FieldName IN (' + #PivotCols + N')
) p'
EXEC sp_executesql #PivotQuery
Please try this code:
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME(FieldName)
FROM (SELECT distinct p.FieldName FROM Tablename AS p
) AS x;
SET #sql = N'
SELECT ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT p.Value, p.FieldName, p.RowPos
FROM Tablename AS p
) AS j
PIVOT
(
MAX(Value) FOR FieldName IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
PRINT #sql;
EXEC sp_executesql #sql;

Pivot Rows to Columns Dynamically - SQL

/****** Script for SelectTopNRows command from SSMS ******/
declare #ActivityYear int = 2014
declare #ActivityYear1 int = 2015
declare #ActivityMonth int = 1
declare #ActivityMonth1 int = 3
Select FinancialCategory, ID, (CONVERT(varchar(5), ActivityMonth) + '-'
+ CONVERT(varchar(5), ActivityYear)) As [Month-Year], Sum(HoursCharged) As [Hours]
FROM Forecast
where (ActivityMonth between #ActivityMonth and #ActivityMonth1)
AND (ActivityYear between #ActivityYear and #ActivityYear1)
AND FinancialCategory = 'Forecast'
Group By FinancialCategory, ID,ActivityMonth, ActivityYear
This Outputs a table that looks like this:
And I would like to transpose it to have the hours for each ID broken out by the dates in the range. Note: this range of dates will be dynamic, I set initial dates for testing purposes.
I learnt a bit about dynamic pivot recently, this post helped a lot. As a practice I converted yours, which I think would look like this, but isn't tested as I haven't time tcreate tables etc at the moment. HTH.
declare #ActivityYear int = 2014
declare #ActivityYear1 int = 2015
declare #ActivityMonth int = 1
declare #ActivityMonth1 int = 3
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME((CONVERT(varchar(5), ActivityMonth) + '-'
+ CONVERT(varchar(5), ActivityYear)))
FROM Forecast
WHERE (ActivityMonth between #ActivityMonth and #ActivityMonth1)
AND (ActivityYear between #ActivityYear and #ActivityYear1)
AND FinancialCategory = 'Forecast'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT FinancialCategory, ID, ' + #cols + ' FROM
(
SELECT FinancialCategory, ID, (CONVERT(varchar(5), ActivityMonth) + ''-''
+ CONVERT(varchar(5), ActivityYear)) As [Month-Year],HoursCharged
FROM Forecast
WHERE (ActivityMonth between ' + #ActivityMonth + ' and ' + #ActivityMonth1 + ')
AND (ActivityYear between ' + #ActivityYear + ' and ' +
#ActivityYear1 + ')
AND FinancialCategory = ''Forecast''
) x
PIVOT
(
Sum(HoursCharged)
for (CONVERT(varchar(5), ActivityMonth) + ''-''
+ CONVERT(varchar(5), ActivityYear)) in (' + #cols + ')
) p '
execute(#query)

Including default values on dynamic pivot

I am pivoting data in a table based on date, but need to return NULL for dates with no data.
Data in [dbo].[Metrics]:
The dynamic pivot SQL I am executing:
DECLARE #cols NVARCHAR(1000);
SELECT #cols =
STUFF((SELECT N'],[' + month_day
FROM (SELECT DISTINCT RIGHT(CONVERT(NCHAR(10), [Date], 126), 2)
FROM [Metrics]) AS O(month_day)
ORDER BY month_day
FOR XML PATH('')
), 1, 2, '') + N']';
DECLARE #sql NVARCHAR(2000);
SET #sql =
N'SELECT [Key], ' + #cols +
N' FROM (SELECT [Key], ' +
N' RIGHT(CONVERT(NCHAR(10), [Date], 126), 2) AS month_day, ' +
N' [Value] ' +
N' FROM [Metrics]) AS O ' +
N'PIVOT ' +
N'(SUM([Value]) FOR month_day IN (' + #cols + N')) AS P;';
EXECUTE(#sql);
...and the result set that dynamic SQL returns:
As you can see, since I do not have data for every day of the month it is simply not returning columns for those days. How can I have it return columns 10-23 and 28-31 with NULL for each cell? It should return columns 1-31 regardless of the actual month (e.g., 31 columns even when there are less than 31 days in a given month).
Any help is appreciated.
Then you don't really need that columns to be generated dynamically, since you always want them from 1 to 31. The easier way would be hardcoding those values on your pivot, really. Anyway, here is the way you can define your columns dynamically and still get all days:
SELECT #cols =
STUFF((SELECT N'],[' + CAST(number AS VARCHAR(2))
FROM (SELECT DISTINCT number
FROM master..[spt_values]
WHERE number BETWEEN 1 AND 31) AS O(number)
ORDER BY number
FOR XML PATH('')
), 1, 2, '') + N']';
And the version you really should be using is this:
SELECT *
FROM (SELECT [Key],
RIGHT(CONVERT(NCHAR(10), [Date], 126), 2) AS month_day,
[Value]
FROM [Metrics]) AS O
PIVOT (SUM([Value]) FOR month_day IN ([01],[02],[03],[04],[05],[06],[07],[08],[09],
[10],[11],[12],[13],[14],[15],[16],[17],[18],
[19],[20],[21],[22],[23],[24],[25],[26],[27],
[28],[29],[30],[31])) AS P;

Rows To Columns in SQL SERVER USING PIVOT Command (Replacing NULL Values To 0 & Display SUM Of ALL VALUES)

I want to display rows to columns in Sql Server. I have seen the other questions but those columns are hardcoded in the pivot but my columns will be dynamic. What I have achieved till now. As shown in the screenshot I am able to convert the rows into columns but few things I am not able to accomplish.. Need your guyz help
Replacing NULL To 0 in All the Columns
Need to Add 1 more column which will show the sum of all Columns except the companyID
My SQL code:
DECLARE #Columns VARCHAR(MAX)
DECLARE #Convert VARCHAR(MAX)
SELECT #Columns = STUFF((
SELECT '],[' + ErrClassfn
from ArchimedesTables.dbo.PM_ErrClassificationSetup
WHERE CONVERT(VARCHAR(10), ISNULL(EndDate, GETDATE()), 101)
Between CONVERT(VARCHAR(10), GETDATE(), 101)
AND CONVERT(VARCHAR(10), GETDATE(), 101)
ORDER BY '],[' + CONVERT(VARCHAR(MAX), ID) ASC
FOR
XML PATH('')
), 1, 2, '') + ']'
SET #Convert = 'SELECT * INTO #mynewTable FROM
(
SELECT COUNT(WQ.ErrClassfnID) as ErrorCount, UPPER(WQ.CompanyID) as CompanyID,
PME.ErrClassfn as ErrorName
FROM Version25.dbo.WF_Quality AS WQ
LEFT JOIN ArchimedesTables.dbo.PM_ErrClassificationSetup as PME
ON WQ.ErrClassfnID = PME.ID
GROUP BY
UPPER(CompanyID), ErrClassfn
) Quality PIVOT ( SUM(ErrorCount) For ErrorName IN (' + #Columns
+ ')) as PivotTable SeLeCt * FROM #mynewTable'
EXEC(#Convert)
You can alter the columns names, etc for a Dynamic Pivot, similar to this:
DECLARE #ColumnsNull VARCHAR(MAX)
DECLARE #Columns VARCHAR(MAX)
DECLARE #Convert VARCHAR(MAX)
SELECT #ColumnsNull = STUFF((SELECT ', IsNull(' + QUOTENAME(ErrClassfn) +', 0) as ['+ rtrim(ErrClassfn)+']'
from ArchimedesTables.dbo.PM_ErrClassificationSetup
WHERE CONVERT(VARCHAR(10), ISNULL(EndDate, GETDATE()), 101)
Between CONVERT(VARCHAR(10), GETDATE(), 101)
AND CONVERT(VARCHAR(10), GETDATE(), 101)
ORDER BY ID ASC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #Columns = STUFF((
SELECT '],[' + ErrClassfn
from ArchimedesTables.dbo.PM_ErrClassificationSetup
WHERE CONVERT(VARCHAR(10), ISNULL(EndDate, GETDATE()), 101)
Between CONVERT(VARCHAR(10), GETDATE(), 101)
AND CONVERT(VARCHAR(10), GETDATE(), 101)
ORDER BY '],[' + CONVERT(VARCHAR(MAX), ID) ASC
FOR
XML PATH('')
), 1, 2, '') + ']'
SET #Convert = 'SELECT CompanyID, '+ #ColumnsNull + '
INTO #mynewTable
FROM
(
SELECT COUNT(WQ.ErrClassfnID) as ErrorCount, UPPER(WQ.CompanyID) as CompanyID,
PME.ErrClassfn as ErrorName
FROM Version25.dbo.WF_Quality AS WQ
LEFT JOIN ArchimedesTables.dbo.PM_ErrClassificationSetup as PME
ON WQ.ErrClassfnID = PME.ID
GROUP BY
UPPER(CompanyID), ErrClassfn
) Quality PIVOT ( SUM(ErrorCount) For ErrorName IN (' + #Columns
+ ')) as PivotTable SeLeCt * FROM #mynewTable'
EXEC(#Convert)
I would advise to write the query and get the columns working first, then add the data to a #temp table. It will be easier to debug that way.
You can also create a SUM() field the same way, where you build it dynamically and then add it at in the final SELECT:
So it could be something like this that you could add to the final SELECT:
SELECT #ColumnsTotal = STUFF((SELECT '+' + QUOTENAME(ErrClassfn)
from ArchimedesTables.dbo.PM_ErrClassificationSetup
WHERE CONVERT(VARCHAR(10), ISNULL(EndDate, GETDATE()), 101)
Between CONVERT(VARCHAR(10), GETDATE(), 101)
AND CONVERT(VARCHAR(10), GETDATE(), 101)
ORDER BY ID ASC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')