I have created a pivot table with the following code:
DECLARE #SQL AS VARCHAR(MAX)
DECLARE #Columns AS VARCHAR (MAX)
SELECT #Columns =
COALESCE(#Columns + ', ','')+ QUOTENAME(PortfolioID)
FROM
(
SELECT PortfolioID FROM InfoPortal.DBO.fn_Generic_PortfolioGroupMembers (#PortfolioGroup)
) AS B
ORDER BY B.PortfolioID
SET #SQL = '
WITH PivotData AS
(
SELECT
PortfolioID, Percentage, DurationBand, DurationSort
FROM #Worktable
)
SELECT
DurationBand,
' + #Columns + '
FROM PivotData
PIVOT
(
SUM(Percentage)
FOR PortfolioID
IN (' + #Columns + ')
) AS PivotResult
ORDER BY DurationSort'
EXEC (#SQL)
However what i would like to do is add grand totals for each portfolioID, and i'm unsure of how to achieve this. Any help?
You should be able to add a GROUP BY with ROLLUP to produce the totals row, similar to this:
DECLARE #SQL AS VARCHAR(MAX)
DECLARE #Columns AS VARCHAR (MAX)
DECLARE #ColumnsRollup AS VARCHAR (MAX)
SELECT #Columns =
COALESCE(#Columns + ', ','')+ QUOTENAME(PortfolioID)
FROM
(
SELECT distinct PortfolioID
FROM worktable
) AS B
ORDER BY B.PortfolioID
SELECT #ColumnsRollup =
COALESCE(#ColumnsRollup + ', Sum(','Sum(')+ QUOTENAME(cast(PortfolioID as varchar(10)))+') as Portfolio'+cast(PortfolioID as varchar(10))
FROM
(
SELECT distinct PortfolioID
FROM worktable
) AS B
ORDER BY B.PortfolioID
SET #SQL = '
WITH PivotData AS
(
SELECT PortfolioID, Percentage, DurationBand, DurationSort
FROM Worktable
)
SELECT case when DurationBand is not null then cast(durationband as varchar(10))
else ''Total'' end Durationband, ' + #ColumnsRollup+ '
FROM
(
SELECT DurationBand, ' + #Columns + '
FROM PivotData
PIVOT
(
SUM(Percentage)
FOR PortfolioID IN (' + #Columns + ')
) AS PivotResult
) src
GROUP BY DurationBand with ROLLUP'
EXEC (#SQL)
See SQL Fiddle with Demo. Note: sample data is just mocked based on your table structure.
Result:
| DURATIONBAND | PORTFOLIO1 | PORTFOLIO2 | PORTFOLIO3 |
-------------------------------------------------------
| 2 | 78 | (null) | (null) |
| 5 | (null) | (null) | 4 |
| 12 | 10 | 45 | (null) |
| Total | 88 | 45 | 4 |
Based in the need to keep things sorted, you can use:
DECLARE #SQL AS VARCHAR(MAX)
DECLARE #Columns AS VARCHAR (MAX)
DECLARE #ColumnsRollup AS VARCHAR (MAX)
SELECT #Columns =
COALESCE(#Columns + ', ','')+ QUOTENAME(PortfolioID)
FROM
(
SELECT distinct PortfolioID
FROM worktable
) AS B
ORDER BY B.PortfolioID
SELECT #ColumnsRollup =
COALESCE(#ColumnsRollup + ', Sum(','Sum(')+ QUOTENAME(cast(PortfolioID as varchar(10)))+') as '+QUOTENAME(cast(PortfolioID as varchar(10)))
FROM
(
SELECT distinct PortfolioID
FROM worktable
) AS B
ORDER BY B.PortfolioID
SET #SQL = '
WITH PivotData AS
(
SELECT PortfolioID, Percentage, DurationBand, DurationSort
FROM Worktable
)
SELECT *
FROM
(
SELECT DurationBand, ' + #Columns + '
FROM PivotData
PIVOT
(
SUM(Percentage)
FOR PortfolioID IN (' + #Columns + ')
) AS PivotResult
UNION ALL
select ''Grand Total'', '+#ColumnsRollup+'
from
(
SELECT DurationBand, ' + #Columns + '
FROM PivotData
PIVOT
(
SUM(Percentage)
FOR PortfolioID IN (' + #Columns + ')
) AS PivotResult
)tot
) src
'
EXEC (#SQL)
See SQL Fiddle with Demo
Related
At the top the result of my pivot and below is the original table
I want a result like this
AssetId | 77 | 78 | 79 | 80 | 81 | 82
1571539 | 99 |01/01/2020 | L460 | Avast Pro| 01/30/2020| NULL
8323444 | 103|01/28/2020 |PhoneI| Avast Pro| Null | NULL
#pAssetTypeId int
as
IF OBJECT_ID('tempdb..##TBL_TEMP') IS NOT NULL
DROP TABLE #TBL_TEMP
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
DECLARE #AssetId AS Varchar(MAX)
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME(fm.FieldMaintenanceId)
FROM dbo.FieldData f
LEFT JOIN dbo.AssetType a
ON f.AssetTypeId = a.AssetTypeId
LEFT JOIN dbo.FieldMaintenance fm
ON f.FieldMaintenanceId = fm.FieldMaintenanceId
where a.AssetTypeId = #pAssetTypeId
SET #SQLQuery =
N'SELECT AssetId,' + #PivotColumns + '
INTO #TBL_TEMP
FROM [dbo].[AssetDetails] a
PIVOT( MAX([Data])
FOR [FieldMaintenanceId] IN (' + #PivotColumns + ')) as Q'
EXEC sp_executesql #SQLQuery
Select * from #TBL_TEMP
Click to see desired pivot result
IF OBJECT_ID('tempdb..##TBL_TEMP') IS NOT NULL
DROP TABLE ##TBL_TEMP
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
DECLARE #AssetId AS Varchar(MAX)
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') +
QUOTENAME(fm.FieldMaintenanceId)
FROM CONCEPASSET.dbo.FieldData f
LEFT JOIN CONCEPASSET.dbo.AssetType a
ON f.AssetTypeId = a.AssetTypeId
LEFT JOIN CONCEPASSET.dbo.FieldMaintenance fm
ON f.FieldMaintenanceId = fm.FieldMaintenanceId
where a.AssetTypeId = 3
SET #SQLQuery =
N'Select *
from
(
Select
AssetId,
AssetTypeId,
FieldMaintenanceId,
[Data]
from AssetDetails ) AS SourceTable PIVOT(MAX([Data]) FOR [FieldMaintenanceId]
IN('+#PivotColumns+')) AS TableTest '
EXEC sp_executesql #SQLQuery
Reworked the code and it worked thank you all!!
Image transcription:
FTE/RATE CARD | 2018-01-01 | 2018-02-01 | 2018-03-01 | 2018-04-01 | 2018-05-01 | 2018-06-01 | ...
-------------------------------------------------------------------------------------------------
FTE 3 | NULL | NULL | NULL | 33 | 38 | 40.5 | ...
I have a pivot sql query as below.
I need to replace NULL values with Zeros in the result.
I don't know where do I exactly use the IsNull or Coalesce function in the query.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF
(
(
SELECT distinct ',' + QUOTENAME([MONTH])
FROM [HEADCOUNT]
WHERE [MONTH] BETWEEN '01-012018' AND '12-01-2018'
ORDER BY ',' + QUOTENAME([MONTH])
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''
)
SET #query = 'SELECT [FTE/RATE CARD],' + #cols + ' FROM
(
SELECT [MONTH],[FTE/RATE CARD],[HC]
FROM [HEADCOUNT]
WHERE [CC-LOC] IN ([CC-LOC])
) x pivot (Sum ([HC]) for [MONTH] in (' + #cols + '))p' execute(#query)
You'll need to generate another column list for the select statement, and wrap individual months with ISNULL, like so:
DECLARE #cols AS NVARCHAR(MAX),
#selectCols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #selectCols = STUFF
(
(
SELECT distinct ', ISNULL(' + QUOTENAME([MONTH]) + ', 0) AS ' + QUOTENAME([MONTH])
FROM [HEADCOUNT]
WHERE [MONTH] BETWEEN '01-012018' AND '12-01-2018'
ORDER BY ',' + QUOTENAME([MONTH])
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''
)
SELECT #cols = STUFF
(
(
SELECT distinct ',' + QUOTENAME([MONTH])
FROM [HEADCOUNT]
WHERE [MONTH] BETWEEN '01-012018' AND '12-01-2018'
ORDER BY ',' + QUOTENAME([MONTH])
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''
)
SET #query = 'SELECT [FTE/RATE CARD],' + #selectCols + ' FROM
(
SELECT [MONTH],[FTE/RATE CARD],[HC]
FROM [HEADCOUNT]
WHERE [CC-LOC] IN ([CC-LOC])
) x pivot (Sum ([HC]) for [MONTH] in (' + #cols + '))p' execute(#query)
This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 6 years ago.
I'm struggling with an error while making a dynamic pivot table
The source data is
JobID | SalesForMonth | YearMonth
7734 | 400 | 2016-12
7734 | 350 | 2017-01
8540 | 444 | 2016-12
8540 | 300 | 2017-01
and aiming for
JobID | 2016-12 | 2017-01
7734 | 400 | 350
8540 | 444 | 300
and I've tried to use a query I found on here to create the column headers. But must admit I don't really understand the 'For XML' line and getting a syntax error there on line 6
DECLARE
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(YearMonth)
FROM v_JobSalesByMonth
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
SELECT #query =
'SELECT * FROM
(SELECT JobID, YearMonth, SalesForMonth
FROM v_JobSalesByMonth) X
PIVOT
(
(JobID, SalesForMonth)
for [YearMonth] in (' + #cols + ')
) P'
I'd also like to stick in a 'total sales' for the jobID column
Any help would be much appreciated
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(YearMonth) From v_JobSalesByMonth Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [JobID],[TotalSales],' + #SQL + '
From (
Select JobID
,TotalSales = sum(SalesForMonth) over (Partition By JobID)
,YearMonth
,SalesForMonth
From v_JobSalesByMonth A
) A
Pivot (sum(SalesForMonth) For [YearMonth] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
EDIT - Dynamically Create View
Since you can't have dynamic SQL in a view, you could have job scheduled (daily or monthly) to drop and re-create the view.
if object_id('vw_SalesByMonth','v') is not null
drop view vw_SalesByMonth;
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(YearMonth) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Create View vw_SalesByMonth
AS
Select [JobID],[TotalSales],' + #SQL + '
From (
Select JobID
,TotalSales = sum(SalesForMonth) over (Partition By JobID)
,YearMonth
,SalesForMonth
From YourTable A
) A
Pivot (sum(SalesForMonth) For [YearMonth] in (' + #SQL + ') ) p'
Exec(#SQL);
Select * from vw_SalesByMonth
Given a table that has unknown column names, how can I list all column names and the amount of rows that have a non-empty (NULL or empty string) value in that column?
Small example:
Col1 | Col2 | Col3 | dkasldk | dD? d3# !(
1 | | 2 | |
| 2 | d | ddd939 |
f | f | 84 | |
Should yield:
Column Name | Values
Col1 | 2
Col2 | 2
Col3 | 3
dkasldk | 1
dD? d3# !( | 0
I already tried around with using INFORMATION_SCHEMA.COLUMNS and creating dynamic SQL queries, but to no avail:
select N'select c.COLUMN_NAME,
(select sum(case when p.' + QUOTENAME(c.COLUMN_NAME) + ' != '''' then 1 else 0 end) from [Produkte] p) [Produkte]
from INFORMATION_SCHEMA.COLUMNS c
where c.TABLE_NAME = ''Produkte'' and c.COLUMN_NAME = ''' + c.COLUMN_NAME + '''' select_statement
from INFORMATION_SCHEMA.COLUMNS c
where c.TABLE_NAME = 'Produkte'
I cannot quite wrap my head around how to combine the two problems (for which I did find solution in their own, but not the combination) of Dynamic Column Names and Count empty values...
You can do something like this using UNPIVOT..
DECLARE #TableName NVARCHAR(MAX) = N'Produkte',
#CountColumns NVARCHAR(MAX),
#Columns NVARCHAR(MAX),
#Sql NVARCHAR(MAX)
SELECT #CountColumns = COALESCE(#CountColumns + ',', '') + 'COUNT(NULLIF(' + QUOTENAME(c.COLUMN_NAME) + ','''')) AS ' + QUOTENAME(c.COLUMN_NAME),
#Columns = COALESCE(#Columns + ',', '') + QUOTENAME(c.COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = #TableName
SET #SQL = 'SELECT [Column Name], [Values]
FROM (
SELECT '
+ #CountColumns + '
FROM ' + #TableName + '
) t
UNPIVOT (
[Values]
FOR [Column Name] IN (' + #Columns + ')
) up
'
EXEC(#SQL)
SQL Fiddle Demo
You can use a dynamic UNPIVOT.
Using these variables:
DECLARE #qry NVARCHAR(MAX)
DECLARE #cols NVARCHAR(MAX) = ''
you can select columns names in a format suitable for the UNPIVOT operation with the following dynamic sql:
SELECT #cols = STUFF((SELECT ',[' + c.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = 'Produkte'
FOR XML PATH('')), 1, 1, '')
Finally use #cols to build the query:
SET #qry = 'SELECT t.Col, COUNT(*)
FROM (SELECT Val, Col
FROM Produkte
UNPIVOT (
Val FOR Col IN (' + #cols + ')) unpvt
) AS t
WHERE t.Val <> '''' OR t.Val IS NOT NULL
GROUP BY t.Col'
... and execute it to get desired result:
EXEC(#qry)
Ultimately the SQL you are after is something like this:
SELECT ColumnName, NonEmpty
FROM ( SELECT A = 1,
[Col1] = COUNT(CASE WHEN [Col1] <> '' THEN 1 END),
[Col2] = COUNT(CASE WHEN [Col2] <> '' THEN 1 END),
[Col3] = COUNT(CASE WHEN [Col3] <> '' THEN 1 END),
[dkasldk] = COUNT(CASE WHEN [dkasldk] <> '' THEN 1 END),
[dD? d3# !(] = COUNT(CASE WHEN [dD? d3# !(] <> '' THEN 1 END)
FROM #T
) AS t
UNPIVOT
( NonEmpty
FOR ColumnName IN ([Col1],[Col2],[Col3],[dkasldk],[dD? d3# !(])
) AS upvt;
UNPIVOT Will convert your data from
Col1 Col2 Col3
-------------------------
1 3 2
To the format you require:
ColumnName NonEmpty
-------------------------
Col1 1
Col2 3
Col3 2
You can build up dynamically using something like this:
-- SAMPLE DATA
USE TempDB;
CREATE TABLE #T
(
[Col1] VARCHAR(1),
[Col2] VARCHAR(1),
[Col3] VARCHAR(2),
[dkasldk] VARCHAR(6),
[dD? d3# !(] VARCHAR(1)
);
INSERT #T ([Col1], [Col2], [Col3], [dkasldk], [dD? d3# !(])
VALUES
('1', NULL, '2', NULL, ''),
('', '2', 'd', 'ddd939', NULL),
('f', 'f', '84', NULL, '');
DECLARE #TableName SYSNAME = '#T';
-- VALID INPUT TABLE
IF OBJECT_ID(#TableName, 'U') IS NULL
AND OBJECT_ID(#TableName, 'V') IS NULL
BEGIN
PRINT 'Invalid table or View';
RETURN
END
-- BUILD DYNAMIC SQL
DECLARE #SQL NVARCHAR(MAX) =
CONCAT('SELECT ColumnName, NonEmpty FROM (SELECT A = 1, ' ,
STUFF(( SELECT CONCAT(',',
QUOTENAME(name),
' = COUNT(CASE WHEN ',
QUOTENAME(Name),
' <> '''' THEN 1 END)')
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
' FROM ',
#TableName,
') AS t UNPIVOT (NonEmpty FOR ColumnName IN (',
STUFF(( SELECT CONCAT(',', QUOTENAME(name))
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
')) AS upvt');
-- EXECUTE DYNAMIC SQL
EXECUTE sp_executesql #SQL;
select A.QueId,Question,Answer from
ServayQues1 as S inner join ServayQuesAns1 as A
on S.QueId=A.QueId
QueId | Question | Answer
----------------------------
1 test Ok
1 test not bad
1 test need to be improve
----------------------------
My expected output is below
QueId | Question | answer1 | answer2 | answer3
--------------------------------------------------------------------------
1 test Ok not bad need to be improve
---------------------------------------------------------------------------
How do I get this output?
Hope this is what you need.
DECLARE #PivotColumnHeaders VARCHAR(MAX)
SELECT #PivotColumnHeaders = COALESCE(
#PivotColumnHeaders + ',[' + cast([answer] as varchar) + ']',
'[' + cast([answer] as varchar)+ ']')
FROM (SELECT DISTINCT Answer from
ServayQues1 as S inner join ServayQuesAns1 as A
on S.QueId=A.QueId) s
DECLARE #PivotTableSQL NVARCHAR(MAX)
SET #PivotTableSQL = N'
SELECT *
FROM
(
SELECT A.QueId,Question,Answer
FROM ServayQuesAns1 A inner join
ServayQues1 q on q.QueId= a.QueId
) AS source
PIVOT
(
MAX([Answer])
FOR [Answer] IN (
' + #PivotColumnHeaders + '
)
) AS PivotTable
'
print #PivotTableSQL
EXECUTE(#PivotTableSQL)