SAMPLE TABLE
SELECT * INTO #TEMP
FROM
(
SELECT 'Site 1' [Site], 'Form A' [FormID], 'Yes' Present
UNION ALL
SELECT 'Site 1', 'Form B', 'Yes'
UNION ALL
SELECT 'Site 1', 'Form C', 'Yes'
UNION ALL
SELECT 'Site 1', 'Form B', 'NO'
UNION ALL
SELECT 'Site 1', 'Form C', 'NO'
UNION ALL
SELECT 'Site 2', 'Form A', 'Yes'
UNION ALL
SELECT 'Site 2', 'Form A', 'Yes'
UNION ALL
SELECT 'Site 2', 'Form B', 'Yes'
UNION ALL
SELECT 'Site 2', 'Form B', 'NO'
UNION ALL
SELECT 'Site 2', 'Form C', 'Yes'
UNION ALL
SELECT 'Site 3', 'Form B', 'Yes'
UNION ALL
SELECT 'Site 3', 'Form A', 'Yes'
UNION ALL
SELECT 'Site 3', 'Form C', 'Yes'
UNION ALL
SELECT 'Site 3', 'Form A', 'Yes'
)TAB
1. Row and Column Total
-- Get the columns for dynamic pivot
DECLARE @cols NVARCHAR (MAX)
SELECT @cols = COALESCE (@cols + ',[' + [Site] + ']', '[' + [Site] + ']')
FROM (SELECT DISTINCT [Site] FROM #TEMP WHERE Present='YES') PV
ORDER BY [Site]
-- Since we need Total in last column, we append it at last
SELECT @cols += ',[Total]'
You can use CUBE
to get the row and column total on pivoting. More about CUBE
here.
DECLARE @query NVARCHAR(MAX)
SET @query = 'SELECT FORMID,' + @cols + ' FROM
(
SELECT
ISNULL([SITE],''Total'')[SITE],
SUM(CNT)CNT ,
ISNULL(FORMID,''Total'')FORMID
FROM
(
SELECT DISTINCT [SITE],FORMID,
COUNT(FORMID) OVER(PARTITION BY [SITE],FORMID) CNT
FROM #TEMP
WHERE PRESENT=''YES''
)TAB
GROUP BY [SITE],FORMID
WITH CUBE
) x
PIVOT
(
MIN(CNT)
FOR [SITE] IN (' + @cols + ')
) p
ORDER BY CASE WHEN (FORMID=''Total'') THEN 1 ELSE 0 END,FORMID'
EXEC SP_EXECUTESQL @query
2. Row Total only
You can use ROLLUP
to get the row total.
-- Get the columns for dynamic pivot
DECLARE @cols NVARCHAR (MAX)
SELECT @cols = COALESCE (@cols + ',[' + [Site] + ']', '[' + [Site] + ']')
FROM (SELECT DISTINCT [Site] FROM #TEMP WHERE Present='YES') PV
ORDER BY [Site]
DECLARE @query NVARCHAR(MAX)
SET @query = 'SELECT FORMID,' + @cols + ' FROM
(
SELECT
ISNULL([SITE],''Total'')[SITE],
SUM(CNT)CNT ,
ISNULL(FORMID,''Total'')FORMID
FROM
(
SELECT DISTINCT [SITE],FORMID,
COUNT(FORMID) OVER(PARTITION BY [SITE],FORMID) CNT
FROM #TEMP
WHERE PRESENT=''YES''
)TAB
GROUP BY [SITE],FORMID
WITH ROLLUP
) x
PIVOT
(
MIN(CNT)
FOR [SITE] IN (' + @cols + ')
) p
ORDER BY CASE WHEN (FORMID=''Total'') THEN 1 ELSE 0 END,FORMID'
EXEC SP_EXECUTESQL @query
3. Column Total only
Change GROUP BY [SITE],FORMID
to GROUP BY FORMID,[SITE]
-- Get the columns for dynamic pivot
DECLARE @cols NVARCHAR (MAX)
SELECT @cols = COALESCE (@cols + ',[' + [Site] + ']', '[' + [Site] + ']')
FROM (SELECT DISTINCT [Site] FROM #TEMP WHERE Present='YES') PV
ORDER BY [Site]
--Since we need Total in last column, we append it at last
SELECT @cols += ',[Total]'
DECLARE @query NVARCHAR(MAX)
SET @query = 'SELECT FORMID,' + @cols + ' FROM
(
SELECT
ISNULL([SITE],''Total'')[SITE],
SUM(CNT)CNT ,
ISNULL(FORMID,''Total'')FORMID
FROM
(
SELECT DISTINCT [SITE],FORMID,
COUNT(FORMID) OVER(PARTITION BY [SITE],FORMID) CNT
FROM #TEMP
WHERE PRESENT=''YES''
)TAB
GROUP BY FORMID,[SITE]
WITH ROLLUP
) x
PIVOT
(
MIN(CNT)
FOR [SITE] IN (' + @cols + ')
) p
WHERE FORMID <> ''Total''
ORDER BY FORMID'
EXEC SP_EXECUTESQL @query
Now, if you want to replace null with zero, you can use the below code before dynamic pivot.
DECLARE @NulltoZeroCols NVARCHAR (MAX)
SELECT @NullToZeroCols = SUBSTRING((SELECT ',ISNULL(['+[Site]+'],0) AS ['+[Site]+']'
FROM (SELECT DISTINCT [Site] FROM #TEMP)TAB
ORDER BY [Site] FOR XML PATH('')),2,8000)
SELECT @NullToZeroCols += ',ISNULL([Total],0) AS [Total]'
And in the out most query of dynamic pivot, replace @cols
variable with @NullToZeroCols