Dynamic pivot table or what? - sql

I have a table that consists of pageno int, groupid varchar(10). Each row in the table is distinct. There can be multiple groupid and pageno combinations. The amount of pagenos is unknown and the amount of groupids is unknown. The data might look like this:
pageno groupid
101105 mpadilla
101105 gentry
100100 mpadilla
100100 gentry
I would like to have a result set returned that shows the pagenos as columns and the groupids as rows and an x where they intersect (meaning it exists for that pageno/groupid combo).
Would I use sql pivot for this? If so, give me an example please. If not, please provide your example and explanation. I'm a little confused.

Your requirements are not entirely clear but if I am understanding your question you can use the PIVOT function to get the result.
The basic syntax for this query of query would be the following if you had a limited number of pagenos:
select groupid, [101105], [100100]
from
(
select pageno, groupid,
flag = 'X'
from yourtable
) d
pivot
(
max(flag)
for pageno in ([101105], [100100])
) piv;
See SQL Fiddle with Demo.
Then if you have an unknown number of values, you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(pageno)
from yourtable
group by pageno
order by pageno
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT groupid, ' + #cols + '
from
(
select pageno, groupid,
flag = ''X''
from yourtable
) x
pivot
(
max(flag)
for pageno in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. Both versions will give a result:
| GROUPID | 100100 | 101105 |
| gentry | X | X |
| mpadilla | X | X |
| test | X | (null) |
Edit, if you have nulls that you want to replace, then I would create a second list of column names this one will be used for the final select list and it will include coalesce to replace the null. The code will be:
DECLARE #cols AS NVARCHAR(MAX),
#colsNull AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(pageno)
from yourtable
group by pageno
order by pageno
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsNull = STUFF((SELECT ', coalesce(' + QUOTENAME(pageno) +', '''') as '+QUOTENAME(pageno)
from yourtable
group by pageno
order by pageno
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT groupid, ' + #colsNull + '
from
(
select pageno, groupid,
flag = ''X''
from yourtable
) x
pivot
(
max(flag)
for pageno in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. The result will be:
| GROUPID | 100100 | 101105 |
| gentry | X | X |
| mpadilla | X | X |
| test | X | |

I answered my own question. What I did was add an additional column called customized to my original table. This column only contains an x for each customized page. Kind of redundant I know but it seems that the pivot table needs 3 columns. So here is my dynamic code that I came up with and works:
DECLARE #query VARCHAR(4000), #groupids VARCHAR(8000)
SELECT #groupids = STUFF(( SELECT DISTINCT
'],[' + LTRIM(groupid)
FROM DistinctPages
ORDER BY '],[' + LTRIM(groupid)
FOR XML PATH('')
), 1,2, '') + ']'
SET #query =
'SELECT * FROM (SELECT pageno, groupid, customized FROM DistinctPages)t
PIVOT (MAX(customized) FOR groupid
IN ('+#groupids+')) AS CustomizedPagesPerGroups'
EXECUTE (#query)

Related

How to display columns as rows in a SQL query?

I'm trying to display exam results form simple database containing two tables tblStudents and tblExamResults.
tblstudents contains student ID and Full_Name columns
In tblexamResults columns are Student_id, Subject and Marks.
as in below Picture
1- Currently I am displaying Student results using this query
SELECT tblStudents.Full_Name, tblExamResults.Subject, tblExamResults.Marks
FROM tblExamResults INNER JOIN
tblStudents ON tblExamResults.Student_id = tblStudents.Student_ID
order by tblStudents.Full_Name
2 - And results looks like in the following picture:
3 - But what I want is to display each subject as row and get result of each subject below it
So that each student's result is displayed in the same row:
Student_Name sub1_result sub2_Result sub3_Result
Like in the following picture (Excel screenshot)
So:
How I can display data in that format?
Is that possible in SQL Server?
select fullname,[english] english, [history] history, [physics] physics
from
(
select fullname,subject,marks
from (yourquery)
) src
pivot
(
max(marks)
for subject in ([english], [history], [physics])
) piv;
or
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.subject)
FROM (yourquery) c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT fullname, ' + #cols + ' from
(
select fullname,subject,marks
from (your query)
) x
pivot
(
max(marks)
for subject in (' + #cols + ')
) p '
execute(#query)
fullname english history physics
a 85 70 60
i 60 100 89
s 90 90 99
Finally I've used next part of #Chanukya Answer with little Changes
in that Answer i was getting error at line 5 becouse of parentheses in FROM (yourquery) c
Declare #query nvarchar(max);
DECLARE #cols AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.subject)
FROM tblExamResults c ' parentheses Removed from (tblExamResults) c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
print(#cols)
set #query = '
SELECT *
FROM (
SELECT tblStudents.Full_Name, tblExamResults.Subject, tblExamResults.Marks
FROM tblExamResults INNER JOIN
tblStudents ON tblExamResults.Student_id = tblStudents.Student_ID
) as s
PIVOT
(
sum(marks) FOR subject IN ('+ #cols +')
)AS pvt'
;
execute(#query)

How do I create a "Total" column and footer from a pivoted table?

I have an inventory table with warehouse, product, and available columns which looks like:
warehouse product available
John2196 KITCOMP01 7
John2196 KITCOMP01 12
John2196 KITCOMP02 7
JohnS196 KITCOMP01 9
JohnS196 KITCOMP03 1
And the warehouse column is pivoted to create this:
product John2196 JohnS196
KITCOMP01 19 9
KITCOMP02 7 NULL
KITCOMP03 NULL 1
with this code
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Warehouse)
from tlninventory
group by Warehouse
order by Warehouse
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = 'SELECT product,' + #cols + ' from
(
select product, warehouse, available
from tlninventory
) x
pivot
(
sum(available)
for warehouse in (' + #cols + ')
) p '
execute(#query)
Now I need to get a Total Column and Total Footer Row while ignoring NULL values but since the headers are created through the pivot and this query will be run on multiple tables with differing warehouses and products, meaning the headers will never be the same, I can not sum static columns such as this thread suggests.
For reference I need a final table that looks like:
product John2196 JohnS196 Total
KITCOMP01 19 9 28
KITCOMP02 7 NULL 7
KITCOMP03 NULL 1 1
Total 26 10 36
You need to do a few things here.
First create a new variable to store the 'sum(warehouse)' columns
DECLARE #cols AS NVARCHAR(MAX),
#sumCols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
Create the sum columns similar to the regular pivot column names
select #sumCols = STUFF((SELECT ',' + 'SUM(' + QUOTENAME(Warehouse) + ')' + QUOTENAME(Warehouse)
from tlninventory
group by Warehouse
order by Warehouse
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,1,'')
now you need to create a cte of your pivot query, but add a windowed sum() over() to get the row totals..
set #query = ' WITH cte AS
(
SELECT product,' + #cols + ', Total from
(
select product, warehouse, available, sum(available) over (partition by product) total
from tlninventory
) x
pivot
(
sum(available)
for warehouse in (' + #cols + ')
) p
)
now you Union a total column at the end using your sum columns .
set #query = ' WITH cte AS
(
SELECT product,' + #cols + ', Total from
(
select product, warehouse, available, sum(available) over (partition by product) total
from tlninventory
) x
pivot
(
sum(available)
for warehouse in (' + #cols + ')
) p
)
SELECT product,' + #cols + ', Total FROM (
SELECT *, ''a'' sortCol
FROM cte
UNION ALL
SELECT ''Total'', ' + #sumCols + ', sum(total), ''z''
FROM cte
) a
ORDER BY sortCol, product'
execute(#query)
SQL Fiddle

SQL query unknown rows into columns

I asked this question and it was marked as a duplicate of How to pivot unknown number of columns & no aggregate in SQL Server?, but that answer doesn't help me.
I have a table of data that looks like this, with an unknown number of rows and values.
RecID Name Value
1 Color Red
2 Size Small
3 Weight 20lbs
4 Shape Square
I need a query that will return the data like this, building out a column for each row. I cannot hard code anything except the column headers 'Name' and 'Value'.
Color Size Weight Shape
Red Small 20lbs Square
Here is what I have so far that is partly working:
INSERT INTO #Table VALUES
(1,'Color' ,'Red'),
(2,'Size' ,'Small'),
(3,'Weight','20lbs'),
(4,'Shape' ,'Square')
;with mycte
as
(
SELECT rn,cols,val
FROM (SELECT row_number() over(order by Name) rn, Name, Value
FROM #Table) AS src1
UNPIVOT (val FOR cols
IN ( [Name], [Value])) AS unpvt
)
SELECT *
FROM (SELECT rn,cols,val
FROM mycte) AS src2 PIVOT
( Max(val) FOR rn IN ([1], [2], [3])) AS pvt
Which returns:
cols 1 2 3
Name Color Shape Size
Value Red Square Small
Two problems with this that I can't seem to resolve.
I don't need the column headers and the first column that has cols, Name, Value in it.
Can't figure out how to have it build a column for each row without specifying the [x] identifiers.
Any guidance would be great I've been stuck on this a while now.
declare #collist nvarchar(max)
SET #collist = stuff((select distinct ',' + QUOTENAME(name)
FROM #t -- your table here
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #q nvarchar(max)
set #q = '
select *
from (
select rn, name, Value
from (
select *, row_number() over (partition by name order by RecID desc) as rn
from #t -- your table here
) as x
) as source
pivot (
max(Value)
for name in (' + #collist + ')
) as pvt
'
exec (#q)
Until now, I have reached to following code, hope it helps you,
Current outpu comes as
Color Shape Size Weight
Red NULL NULL NULL
NULL NULL Small NULL
NULL NULL NULL 20lbs
NULL Square NULL NULL
Create table DyTable
(
tid int,
Name varchar(20),
Value varchar(20)
)
INSERT INTO DyTable VALUES
(1,'Color' ,'Red'),
(2,'Size' ,'Small'),
(3,'Weight','20lbs'),
(4,'Shape' ,'Square')
DECLARE #Cols NVARCHAR(MAX);
DECLARE #Cols1 NVARCHAR(MAX);
SELECT #Cols = STUFF((
SELECT DISTINCT ', ' + QUOTENAME(Name)
FROM DyTable
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,'')
,#Cols1 = STUFF((
SELECT DISTINCT ', max(' + QUOTENAME(Name) + ') as ' + Name
FROM DyTable
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,'')
DECLARE #Sql NVARCHAR(MAX)
Select #Cols
SET #Sql = 'Select '+ #Cols1 +'
from (SELECT ' + #Cols + '
FROM DyTable t
PIVOT (MAX(Value)
FOR Name
IN (' + #Cols + ')
)P)a'
EXECUTE sp_executesql #Sql

SQL Server 2008 - Dynamic Pivot on one column, group by another, maintain reference to third

I've just started learning SQL and am struggling with dynamically transposing a three column table correctly. I previously hard coded the query, but now need it to be dynamic as the values in my pivot column may change in the future.
Here's my starting point:
questionid | DebriefingQuestionResults | OperationSessionRecordID
------------------------------------------------------------------
32 | 3 | 8071
34 | 0 | 8071
36 | 1 | 8071
32 | 2 | 8074
34 | 6 | 8074
36 | 5 | 8074
And here's what I want to produce:
OperationSessionRecordID | 32 | 34 | 36
----------------------------------------------
8071 | 3 | 0 | 1
8074 | 2 | 6 | 5
There are only three [questionid] values (32, 34, and 36), at the moment but this may change in the future, hence wanting a dynamic query. There are about 12000 [OperationSessionRecordID] values. All columns are of the type int not null.
Based on this answer I've got this so far, but am not sure how to proceed as it throws the error shown below.
USE training_db
--Test to see if table exists, if so drop ready for creation--
IF OBJECT_ID('TheatreMan.DebriefingQuestionsResultsPivoted','U') IS NOT NULL
DROP TABLE TheatreMan.DebriefingQuestionsResultsPivoted
--Declare query and variable names--
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get Distinct values of the PIVOT column--
SET #ColumnName = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.questionid)
FROM dbo.DebriefingQuestionsResultsTEMP
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT OperationSessionRecordID, ' + #ColumnName + '
(select questionid,
DebriefingQuestionResults
OperationSessionRecordID
FROM dbo.DebriefingQuestionsResultsTEMP)
x
PIVOT (
min(DebriefingQuestionResults)
for questionid in (' + #ColumnName + ')
)
AS PIV'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
When I run this it throws this error, so something's obviously wrong with my #ColumnName variable, but I can't work out what it is.
Msg 4104, Level 16, State 1, Line 9
The multi-part identifier "c.questionid" could not be bound.
Any help would be most appreciated!
SB
This should work:
declare #collist nvarchar(max)
SET #collist = stuff((select distinct ',' + QUOTENAME(questionid)
FROM #t -- your table here
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #q nvarchar(max)
set #q = '
select OperationSessionRecordID, ' + #collist + '
from (
select OperationSessionRecordID, DebriefingQuestionResults, questionid
from (
select *
from #t -- your table here
) as x
) as source
pivot (
sum(DebriefingQuestionResults)
for questionid in (' + #collist + ')
) as pvt
'
exec (#q)
try this, I think you just misspelled variable name.
USE training_db
--Test to see if table exists, if so drop ready for creation--
IF OBJECT_ID('TheatreMan.DebriefingQuestionsResultsPivoted','U') IS NOT NULL
DROP TABLE TheatreMan.DebriefingQuestionsResultsPivoted
--Declare query and variable names--
DECLARE # USE training_db
--Test to see if table exists, if so drop ready for creation--
IF OBJECT_ID('TheatreMan.DebriefingQuestionsResultsPivoted','U') IS NOT NULL
DROP TABLE TheatreMan.DebriefingQuestionsResultsPivoted
--Declare query and variable names--
DECLARE #QuestionPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get Distinct values of the PIVOT column--
SET #ColumnName = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.questionid)
FROM dbo.DebriefingQuestionsResultsTEMP
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT OperationSessionRecordID, ' + #ColumnName + '
(select questionid,
DebriefingQuestionResults
OperationSessionRecordID
FROM dbo.DebriefingQuestionsResultsTEMP
ORDER BY OperationSessionRecordID ASC
)x
PIVOT
(
min(DebriefingQuestionResults)
for questionid in (' + #ColumnName + ')
)
AS PIV'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get Distinct values of the PIVOT column--
SET #ColumnName = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.questionid)
FROM dbo.DebriefingQuestionsResultsTEMP
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT OperationSessionRecordID, ' + #ColumnName + '
(select questionid,
DebriefingQuestionResults
OperationSessionRecordID
FROM dbo.DebriefingQuestionsResultsTEMP)
x
PIVOT (
min(DebriefingQuestionResults)
for questionid in (' + #ColumnName + ')
)
AS PIV'
ORDER BY OperationSessionRecordID ASC
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery

How to create a pivot query

Imagine I have this table:
Column A | Column B | Column C
------------------------------
111 X 10
111 Y 12
How can I query this table to show the results like these:
Column A | X | Y
-----------------------------------
111 10 12
You can perform this via a PIVOT. You can use either a static PIVOT where you know the number of columns that you want to rotate or you can use a dynamic PIVOT
Static Pivot (see SQL Fiddle with Demo)
SELECT *
FROM
(
select *
from t1
) x
pivot
(
min(columnc)
for columnb in ([X], [Y])
) p
Dynamic Pivot (see SQL Fiddle with Demo)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(columnb)
from t1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT columna, ' + #cols + ' from
(
select *
from t1
) x
pivot
(
min(ColumnC)
for ColumnB in (' + #cols + ')
) p '
execute(#query)
Both versions will give the same results. The second works when you have an unknown number of columns that will be transformed.
Try:
DECLARE #tbl TABLE (ColumnA INT, ColumnB CHAR(1), ColumnC INT)
INSERT #tbl VALUES (111, 'X', 10), (111, 'Y', 12)
SELECT *
FROM #tbl
PIVOT
(
MAX(ColumnC) FOR ColumnB IN ([X], [Y])
) pvt