Display a different column value in a PIVOT table - sql

I have the following code -
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(Name)
FROM JobPhases
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = 'SELECT *
FROM
(
SELECT c.Registration, p.Name, [x] = 1
FROM JobDetails AS j
INNER JOIN JobPhases p ON p.ID = j.PhaseId
INNER JOIN Jobs job on job.ID = j.JobID
INNER JOIN Cars c on job.CarID = c.ID
) JobDetails
PIVOT
( SUM(x)
FOR Name IN (' + #cols + ')
) pvt'
Which generates this -
JobID | Repair & Reshape | Refit Stripped Parts | Polishing
1000 | id | id | id
1001 | id | id | id
1002 | id | id | id
1003 | id | id | id
1004 | id | id | id
But, I would like it to show j.EstimatedTime instead of j.ID.
The table structure -
JobDetails
ID - PK Auto increment
JobID - Int (Joined to Jobs table)
PhaseID - String (joined to JobPhases table)
EstimatedTime - decimal
JobPhases
ID - PK String
Name - VarChar(150)
Any ideas how I can achieve this?
Thanks

Try changing [x] = 1 to [x] = j.EstimatedTime .

Related

Is there an easy way with (T-)SQL to transform rows into columns like this?

So let's say I have 3 tables like
Questions
========================
id | qtext
========================
1 | "What is 3 x 23?"
------------------------
2 | "Your age?"
Registrants
========================
id | name
========================
1 | "Jason"
------------------------
2 | "Subhasish"
Answers
======================================
registrant_id | question_id | val
======================================
1 | 1 | 69
--------------------------------------
2 | 2 | 45
--------------------------------------
1 | 2 | 26
Is there a way to create from this a table like
AnswersByPartner
==================================================
Name | "What is 3 x 23?" | "Your age?"
==================================================
"Jason" | 69 | 26
--------------------------------------------------
"Subhasish" | NULL | 45
Yes! Here you go (Please check the column names for typo's):
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.qtext)
FROM Questions c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Name, ' + #cols + ' from
(
select r.name, a.val,q.qtext from
registrants r
inner join answers a
on r.id = a.registrant_id
inner join questions q
on q.id = a.question_id
) x
pivot
(
max(val)
for qtext in (' + #cols + ')
) p '
execute(#query)
I always liked doing it like this:
select
R.name as Name,
A1.val as [What is 3 x 23?],
A2.val as [Your age?]
from Registrants R
left join Answers A1 on R.id = A1.registrant_id and A1.question_id = 1
left join Answers A2 on R.id = A2.registrant_id and A2.question_id = 2

SQL SUM in PIVOT that uses MAX

I am trying to get the SUM of 2 or more TIME fields within my PIVOT table, however because the SUM does not work with characters (converted so I can show EstimatedTime / ActualTime), I'm having difficulty.
The code below -
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + ID + ',' + QUOTENAME(Name)
FROM JobPhases
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = 'SELECT * FROM
(
SELECT j.JobID, c.Registration as ''Reg.'', p.Name,
CAST(MAX(j.EstimatedTime) as VARCHAR(MAX)) + ''/'' +
CAST(MAX(j.ActualTime) as VARCHAR(MAX)) as [x]
FROM JobDetails AS j
INNER JOIN JobPhases p ON p.ID = j.PhaseId
INNER JOIN Jobs job on job.ID = j.JobID
INNER JOIN Cars c on job.CarID = c.ID
WHERE (job.Status = 1 or job.Status = 0)
GROUP BY c.Registration, p.Name, j.JobID
) JobDetails
PIVOT
( MAX(x)
FOR Name IN (' + #cols + ')
) pvt'
execute(#query);
Generates -
JobID | Reg. | P13$ | Repair and Reshape | P15$ | Refit Stripped Parts
1065 | BJ11 2VT | NULL | 01:00:00.0000000/01:54:10.5387526 | NULL | NULL
Tables -
**JobDetails**
ID - PK Auto increment
JobID - Int (Joined to Jobs table)
PhaseID - String (joined to JobPhases table)
EstimatedTime - time(7)
ActualTime time(7)
**JobPhases****
ID - PK String
Name - VarChar(150)
The problem in this example is there are 2 JobDetails for JobID 1065 -
ID | JobID | PhaseID | EstimatedTime | ActualTime
25 | 1065 | P13$ | 01:00:00.0000000 | 01:54:10.5387526
26 | 1065 | P13$ | 00:30:00.0000000 | 00:00:00.0000000
So, the correct result should be (Note the 1:30 in the Repair & Reshape) -
JobID | Reg. | P13$ | Repair and Reshape | P15$ | Refit Stripped Parts
1065 | BJ11 2VT | NULL | 01:30:00.0000000/01:54:10.5387526 | NULL | NULL
Any ideas how I can get the total EstimatedTime for all rows for each phase id?
Thanks!
Summing TIME columns isn't straight forward, what you need is to rewrite your line;
CAST(MAX(j.EstimatedTime) as VARCHAR(MAX)) + ''/'' +
to something like (the untested)
CAST(CAST(DATEADD(ms, SUM(DATEDIFF(ms, '0:00:00', j.EstimatedTime)),
'00:00:00') AS TIME) AS VARCHAR(MAX)) + ''/'' +

SQL Server : group by name and specify the values ​​in columns

I´ve got a SQL Server request to get a table with all article features:
SELECT
tartikel.cArtNr AS ID,
tMerkmal.cName AS Feature,
tMerkmalWertSprache.cWert AS FeatureValue
FROM
tartikel
INNER JOIN
tArtikelMerkmal ON tartikel.kArtikel = tArtikelMerkmal.kArtikel
INNER JOIN
tMerkmal ON tArtikelMerkmal.kMerkmal = tMerkmal.kMerkmal
INNER JOIN
tMerkmalWertSprache ON tArtikelMerkmal.kMerkmalWert = tMerkmalWertSprache.kMerkmalWert
WHERE
(tMerkmalWertSprache.kSprache = '1')
I get a result like this:
ID | FeatureName | FeatureValue
--------------------------------
1 | Feature 1 | Value a
1 | Feature 2 | Value a
1 | Feature 2 | Value b
1 | Feature 2 | Value c
1 | Feature 3 | Value a
but I wanted to group by FeatureName and the values in separate columns.
Like this:
ID | FeatureName | FeatureValue 1 | FeatureValue 2 | FeatureValue 3
-------------------------------------------------------------------
1 | Feature 1 | Value a | |
1 | Feature 2 | Value a | Value b | Value c
1 | Feature 3 | Value a | |
How can I modify my request to get the table which is sorted by FeatureName?
You can use the PIVOT function of SQL Server to get the result:
select id, feature, FeatureValue_1, FeatureValue_2, FeatureValue_3
from
(
SELECT tartikel.cArtNr AS ID,
tMerkmal.cName AS Feature,
tMerkmalWertSprache.cWert AS FeatureValue,
'FeatureValue_'+cast(row_number() over(partition by tartikel.cArtNr, tMerkmal.cName
order by tMerkmal.cName) as varchar(10)) seq
FROM tartikel
INNER JOIN tArtikelMerkmal
ON tartikel.kArtikel = tArtikelMerkmal.kArtikel
INNER JOIN tMerkmal
ON tArtikelMerkmal.kMerkmal = tMerkmal.kMerkmal
INNER JOIN tMerkmalWertSprache
ON tArtikelMerkmal.kMerkmalWert = tMerkmalWertSprache.kMerkmalWert
WHERE tMerkmalWertSprache.kSprache = '1'
) d
pivot
(
max(FeatureValue)
for seq in (FeatureValue_1, FeatureValue_2, FeatureValue_3)
) piv;
If you had an unknown number of FeatureValues for each Id, then you could use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ','
+ QUOTENAME('FeatureValue_'+cast(row_number() over(partition by tartikel.cArtNr, tMerkmal.cName
order by tMerkmal.cName) as varchar(10)))
FROM tartikel
INNER JOIN tArtikelMerkmal
ON tartikel.kArtikel = tArtikelMerkmal.kArtikel
INNER JOIN tMerkmal
ON tArtikelMerkmal.kMerkmal = tMerkmal.kMerkmal
INNER JOIN tMerkmalWertSprache
ON tArtikelMerkmal.kMerkmalWert = tMerkmalWertSprache.kMerkmalWert
WHERE tMerkmalWertSprache.kSprache = '1'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, feature,' + #cols + '
from
(
SELECT tartikel.cArtNr AS ID,
tMerkmal.cName AS Feature,
tMerkmalWertSprache.cWert AS FeatureValue,
''FeatureValue_''+cast(row_number() over(partition by tartikel.cArtNr, tMerkmal.cName
order by tMerkmal.cName) as varchar(10)) seq
FROM tartikel
INNER JOIN tArtikelMerkmal
ON tartikel.kArtikel = tArtikelMerkmal.kArtikel
INNER JOIN tMerkmal
ON tArtikelMerkmal.kMerkmal = tMerkmal.kMerkmal
INNER JOIN tMerkmalWertSprache
ON tArtikelMerkmal.kMerkmalWert = tMerkmalWertSprache.kMerkmalWert
WHERE tMerkmalWertSprache.kSprache = ''1''
) x
pivot
(
max(FeatureValue)
for seq in (' + #cols + ')
) p '
execute(#query)

Pivoting 2 columns from 3 Tables and creating pivot-column-names to avoid conflict - SQL-Server 2008R2

Intro and Problem
In my example i have teachers, students and courses.I would like to have an overview which course is teached by whom in which rooms and all the studends in this course. I have the basic setup runnig (with some handcoded statements). But until now i had no luck to prepare the correct STUFF statement:
Prepare #colsStudents so that i can put the name in the column header and remove the need to mess with the ids (adding 100) to avoid a conflict between rooms.id and students.id
Prepare #colsRooms so that i do not have to hardocde the roomnames
Putting i all together by using EXEC sp_executesql #sql;
You can find all sql-statements to create this schema and the data at the end.
Wanted Result Overview Courses,
I would like pivot the columns RoomName and StudentName and use the column values as the new column names. All SQL-Statements to create tables and data are at the end.
Id | Course | Teacher | A3 | E7 | Penny | Cooper | Koothrap. | Amy
---+--------+---------+----+----+-------+--------+-----------+-----+
1 | C# 1 | Marc G. | | 1 | 1 | | |
2 | C# 2 | Sam S. | | 1 | 1 | | 1 |
3 | C# 3 | John S. | 1 | | | 1 | |
4 | C# 3 | Reed C. | | 1 | | | 1 |
5 | SQL 1 | Marc G. | 1 | | | | |
6 | SQL 2 | Marc G. | 1 | | | | |
7 | SQL 3 | Marc G. | | 1 | | 1 | | 1
8 | SQL 3 | Gbn | 1 | | | | 1 |
What i have so far
With PivotData as (
Select cd.Id, c.CourseName as Course, t.TeacherName as Teacher
,r.Id as RoomId, r.RoomName as RoomName
,100 + s.Id as StudentId, s.StudentName as Student
FROM CourseDetails cd
Left JOIN Courses c ON cd.CourseId = c.Id
Left JOIN Teachers t ON cd.TeacherId = t.Id
Left JOIN CourseMember cm ON cd.Id = cm.CourseDetailsId
Left JOIN Students s ON cm.StudentId = s.Id
Left JOIN Rooms r ON cd.RoomId = r.Id
)
Select Course, Teacher
, [1] as A3, [2] as E7 -- RoomColumns
, [101] as Koothrappali, [102] as Cooper, [103] as Penny, [104] as Amy -- StudentColumns
FROM (
Select Course, Teacher, RoomName, RoomId,Student, StudentId
From PivotData) src
PIVOT( Max(RoomName) FOR RoomId IN ([1],[2])) as P1
PIVOT( Count(Student) FOR StudentId IN ([101],[102],[103],[104]) ) as P2
What is missing
The above statement is prepared by hand. Since i do not know the Rooms or Students in advance i need to create the Pivot Statement for the Columns Rooms and Students dynamically. On SO are plenty of examples how to do it. The normal way to do that is to use STUFF:
DECLARE #colsStudents AS NVARCHAR(MAX);
SET #colsStudents = STUFF(
(SELECT N',' + QUOTENAME(y) AS [text()] FROM
(SELECT DISTINCT 100 + Id AS y FROM dbo.Students) AS Y
ORDER BY y
FOR XML PATH('')
),1
,1
,N'');
Select #colsStudents
This returns [101],[102],[103],[104] for the Student Ids. I added 100 to each id to avoid conflicts between the students.id and teh rooms.id column.
As mentioned in the intro i need to dynamically create something like this
[1] as RoomName_1, [2] as RoomName_1 -- RoomColumns
[1] as StudentName1, [2] as StudentName2, ... ,[4] as Amy -- StudentColumns
But all my tries with the stuff statement failed.
All SQL Statements to create the tables and data
CREATE TABLE [dbo].[Teachers](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TeacherName] [nvarchar](120) NULL,
CONSTRAINT PK_Teachers PRIMARY KEY CLUSTERED (Id))
CREATE TABLE [dbo].[Students](
[Id] [int] IDENTITY(1,1) NOT NULL,
[StudentName] [nvarchar](120) NULL,
CONSTRAINT PK_Students PRIMARY KEY CLUSTERED (Id))
CREATE TABLE [dbo].[Courses](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CourseName] [nvarchar](120) NULL,
CONSTRAINT PK_Courses PRIMARY KEY CLUSTERED (Id))
CREATE TABLE [dbo].[Rooms](
[Id] [int] IDENTITY(1,1) NOT NULL,
[RoomName] [nchar](120) NULL,
CONSTRAINT PK_Rooms PRIMARY KEY CLUSTERED (Id))
CREATE TABLE [dbo].[CourseDetails](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CourseId] [int] NOT NULL,
[TeacherId] [int] NOT NULL,
[RoomId] [int] NOT NULL,
CONSTRAINT PK_CourseDetails PRIMARY KEY CLUSTERED (Id),
CONSTRAINT FK_CourseDetails_Teachers_Id FOREIGN Key (TeacherId)
REFERENCES dbo.Teachers (Id),
CONSTRAINT FK_CourseDetails_Courses_Id FOREIGN Key (CourseId)
REFERENCES dbo.Courses (Id),
CONSTRAINT FK_CourseDetails_Rooms_Id FOREIGN Key (RoomId)
REFERENCES dbo.Rooms (Id)
)
CREATE TABLE [dbo].[CourseMember](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CourseDetailsId] [int] NOT NULL,
[StudentId] [int] NOT NULL,
CONSTRAINT PK_CourseMember PRIMARY KEY CLUSTERED (Id),
CONSTRAINT FK_CourseMember_CourseDetails_Id FOREIGN Key (CourseDetailsId)
REFERENCES dbo.CourseDetails (Id),
CONSTRAINT FK_CourseMember_Students_Id FOREIGN Key (StudentId)
REFERENCES dbo.Students (Id)
)
INSERT INTO dbo.Courses (CourseName)
VALUES ('SQL 1 - Basics'),
('SQL 2 - Intermediate'),
('SQL 3 - Advanced'),
('C# 1 - Basics'),
('C# 2 - Intermediate'),
('C# 3 - Advanced')
INSERT INTO dbo.Students (StudentName)
VALUES
('Koothrappali'),
('Cooper'),
('Penny'),
('Amy')
INSERT INTO dbo.Teachers (TeacherName)
VALUES
('gbn '),
('Sam S.'),
('Marc G.'),
('Reed C.'),
('John S.')
INSERT INTO dbo.Rooms (RoomName)
VALUES ('A3'), ('E7')
INSERT [dbo].[CourseDetails] (CourseId, TeacherId, RoomId)
VALUES (4, 3, 2),(5, 2, 2),
(6, 5, 1),(6, 4, 2),
(1,3,1),(2,3,1),(3,3,2),
(3,1,1)
INSERT [dbo].[CourseMember] (CourseDetailsId, StudentId)
VALUES (1,3),(2,3),(2,1),(3,2),(4,1),(7,2),(7,4),(8,1)
I personally would do this a bit different. Since you are trying to pivot two separate columns that screams to use the UNPIVOT function.
The unpivot will convert your multiple columns into rows to then pivot.
Since you have SQL Server 2008, you can use CROSS APPLY and values:
select id, course, teacher, col, flag
from
(
Select cd.Id, c.CourseName as Course, t.TeacherName as Teacher
,cast(r.Id as varchar(10))as RoomId
, r.RoomName as RoomName
,cast(100 + s.Id as varchar(10)) as StudentId
, s.StudentName as Student
, '1' flag
FROM CourseDetails cd
Left JOIN Courses c
ON cd.CourseId = c.Id
Left JOIN Teachers t
ON cd.TeacherId = t.Id
Left JOIN CourseMember cm
ON cd.Id = cm.CourseDetailsId
Left JOIN Students s
ON cm.StudentId = s.Id
Left JOIN Rooms r
ON cd.RoomId = r.Id
) d
cross apply
(
values ('roomname', roomname),('student',student)
) c (value, col)
See Demo. The unpivot generates a result similar to this:
| ID | COURSE | TEACHER | COL | FLAG |
-------------------------------------------------------------
| 1 | C# 1 - Basics | Marc G. | E7 | 1 |
| 1 | C# 1 - Basics | Marc G. | Penny | 1 |
| 2 | C# 2 - Intermediate | Sam S. | E7 | 1 |
| 2 | C# 2 - Intermediate | Sam S. | Penny | 1 |
| 2 | C# 2 - Intermediate | Sam S. | E7 | 1 |
| 2 | C# 2 - Intermediate | Sam S. | Koothrappali | 1 |
| 3 | C# 3 - Advanced | John S. | A3 | 1 |
| 3 | C# 3 - Advanced | John S. | Cooper | 1 |
You will see that the col data contains all the values that you want to pivot. Once the data is in the rows, if will be easy to apply one pivot:
select id, course, teacher,
coalesce(A3, '') A3,
coalesce(E7, '') E7,
coalesce(Koothrappali, '') Koothrappali,
coalesce(Cooper, '') Cooper,
coalesce(Penny, '') Penny,
coalesce(Amy, '') Amy
from
(
select id, course, teacher, col, flag
from
(
Select cd.Id, c.CourseName as Course, t.TeacherName as Teacher
,cast(r.Id as varchar(10))as RoomId
, r.RoomName as RoomName
,cast(100 + s.Id as varchar(10)) as StudentId
, s.StudentName as Student
, '1' flag
FROM CourseDetails cd
Left JOIN Courses c
ON cd.CourseId = c.Id
Left JOIN Teachers t
ON cd.TeacherId = t.Id
Left JOIN CourseMember cm
ON cd.Id = cm.CourseDetailsId
Left JOIN Students s
ON cm.StudentId = s.Id
Left JOIN Rooms r
ON cd.RoomId = r.Id
) d
cross apply
(
values ('roomname', roomname),('student',student)
) c (value, col)
) d
pivot
(
max(flag)
for col in (A3, E7, Koothrappali, Cooper, Penny, Amy)
) piv
See SQL Fiddle with Demo.
Then to convert this to dynamic SQL, you are only pivoting one column, so you will use the following to get the list of columns:
select #cols = STUFF((SELECT ',' + QUOTENAME(col)
from
(
select id, roomname col, 1 SortOrder
from rooms
union all
select id, StudentName, 2
from Students
) d
group by id, col, sortorder
order by sortorder, id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
This will get the list of distinct rooms and students that are then used in the pivot. So the final code will be:
DECLARE #cols AS NVARCHAR(MAX),
#colsNull AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col)
from
(
select id, roomname col, 1 SortOrder
from rooms
union all
select id, StudentName, 2
from Students
) d
group by id, col, sortorder
order by sortorder, id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsNull = STUFF((SELECT ', coalesce(' + QUOTENAME(col)+', '''') as '+QUOTENAME(col)
from
(
select id, roomname col, 1 SortOrder
from rooms
union all
select id, StudentName, 2
from Students
) d
group by id, col, sortorder
order by sortorder, id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT
id, course, teacher,' + #colsNull + '
from
(
select id, course, teacher, col, flag
from
(
Select cd.Id, c.CourseName as Course, t.TeacherName as Teacher
,cast(r.Id as varchar(10))as RoomId
, r.RoomName as RoomName
,cast(100 + s.Id as varchar(10)) as StudentId
, s.StudentName as Student
, ''1'' flag
FROM CourseDetails cd
Left JOIN Courses c
ON cd.CourseId = c.Id
Left JOIN Teachers t
ON cd.TeacherId = t.Id
Left JOIN CourseMember cm
ON cd.Id = cm.CourseDetailsId
Left JOIN Students s
ON cm.StudentId = s.Id
Left JOIN Rooms r
ON cd.RoomId = r.Id
) d
cross apply
(
values (''roomname'', roomname),(''student'',student)
) c (value, col)
) d
pivot
(
max(flag)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo.
Note I implemented a flag to be used in the pivot, this basically generates a Y/N if there is a value for the room or student.
This gives a final result:
| ID | COURSE | TEACHER | A3 | E7 | KOOTHRAPPALI | COOPER | PENNY | AMY |
---------------------------------------------------------------------------------------
| 1 | C# 1 - Basics | Marc G. | | 1 | | | 1 | |
| 2 | C# 2 - Intermediate | Sam S. | | 1 | 1 | | 1 | |
| 3 | C# 3 - Advanced | John S. | 1 | | | 1 | | |
| 4 | C# 3 - Advanced | Reed C. | | 1 | 1 | | | |
| 5 | SQL 1 - Basics | Marc G. | 1 | | | | | |
| 6 | SQL 2 - Intermediate | Marc G. | 1 | | | | | |
| 7 | SQL 3 - Advanced | Marc G. | | 1 | | 1 | | 1 |
| 8 | SQL 3 - Advanced | gbn | 1 | | 1 | | | |
As a side note, this data can also be unpivoted using the unpivot function in sql server. (See Demo with unpivot)
You can create alias string for both pivot columns using dynamic sql query,
For example, for student columns :
DECLARE #colsStudents AS NVARCHAR(MAX),
#colsstudentalias AS NVARCHAR(MAX),
#colsRooms AS NVARCHAR(MAX),
#colsRoomsalias AS NVARCHAR(MAX)
SELECT #colsStudents = STUFF
(
(
SELECT DISTINCT ',' + QUOTENAME(100 + Id)
FROM dbo.Students
FOR XML PATH('')
), 1, 1, ''
)
SELECT #colsstudentalias = STUFF
(
(
SELECT DISTINCT ',' + QUOTENAME(100 + Id)
+ ' as ' + QUOTENAME(ltrim(rtrim(StudentName)))
FROM dbo.Students
FOR XML PATH('')
), 1, 1, ''
)
SELECT #colsRooms = STUFF
(
(
SELECT DISTINCT ',' + QUOTENAME(Id)
FROM dbo.Rooms
FOR XML PATH('')
), 1, 1, ''
)
SELECT #colsRoomsalias = STUFF
(
(
SELECT DISTINCT ',' + QUOTENAME(Id)
+ ' as ' + QUOTENAME(ltrim(rtrim(RoomName)))
FROM dbo.Rooms
FOR XML PATH('')
), 1, 1, ''
)
--SELECT #colsStudents, #colsstudentalias, #colsRooms, #colsRoomsalias
DECLARE #sql varchar(max)
set #sql = ';With PivotData as (
Select cd.Id, c.CourseName as Course, t.TeacherName as Teacher
,r.Id as RoomId, r.RoomName as RoomName
,100 + s.Id as StudentId, s.StudentName as Student
FROM CourseDetails cd
Left JOIN Courses c ON cd.CourseId = c.Id
Left JOIN Teachers t ON cd.TeacherId = t.Id
Left JOIN CourseMember cm ON cd.Id = cm.CourseDetailsId
Left JOIN Students s ON cm.StudentId = s.Id
Left JOIN Rooms r ON cd.RoomId = r.Id
)
Select Course, Teacher
, ' + #colsRoomsalias + '
, ' + #colsstudentalias + '
FROM (
Select Course, Teacher, RoomName, RoomId,Student, StudentId
From PivotData) src
PIVOT( Max(RoomName) FOR RoomId IN (' + #colsRooms + ')) as P1
PIVOT( Count(Student) FOR StudentId IN (' + #colsStudents + ') ) as P2'
exec (#sql)
SQL DEMO
I am going to take a deeper look at both answers above and compare them with the one below.
My problem was in filling the local variables #RoomNames and #StudentNames with the Stuff() Function. One reason was that i had choosen the datatype nchar(120) instead of
nvarchar(120) for the columns StudentName, RoomName.
Another problem i had was that the new columnNames (Student instead of StudentName) where not recognized; therefore i replaced them with * in this statement: Select * From (' + #PivotSrc + N') src
Philip Kelley suggested to use SELECT #RoomIds = isnull(#RoomIds + ',', '') + '[' + Cast(Id as nvarchar(20))+ ']' FROM Rooms instead of STUFF() and since i find it shorter and easier to read i am using it now.
Working Solution
DECLARE #StudentNames NVARCHAR(2000),
#RoomIds NVARCHAR(2000),
#RoomNames NVARCHAR(2000),
#PivotSrc NVARCHAR(MAX),
#PivotBase NVARCHAR(MAX);
SELECT #StudentNames = isnull(#StudentNames + ',', '') + '[' + StudentName + ']' FROM Students
SELECT #RoomIds = isnull(#RoomIds + ',', '') + '[' + Cast(Id as nvarchar(20))+ ']' FROM Rooms
SELECT #RoomNames = isnull(#RoomNames + ',', '') + '[' + RoomName + ']' FROM Rooms
SET #PivotSrc = N'Select cd.Id, c.CourseName as Course, t.TeacherName as Teacher
,r.Id as RoomId, r.RoomName as RoomName
,100 + s.Id as StudentId, s.StudentName as Student
FROM CourseDetails cd
Left JOIN Courses c ON cd.CourseId = c.Id
Left JOIN Teachers t ON cd.TeacherId = t.Id
Left JOIN CourseMember cm ON cd.Id = cm.CourseDetailsId
Left JOIN Students s ON cm.StudentId = s.Id
Left JOIN Rooms r ON cd.RoomId = r.Id'
SET #PivotBase = N' Select Course, Teacher, '
+ #RoomNames + N', '
+ #StudentNames + N' FROM (
Select * From (' + #PivotSrc + N') src
PIVOT( Max(RoomName) FOR RoomName IN ('+#RoomNames+ N')) as P1
PIVOT( Count(Student) FOR Student IN ('+#StudentNames+N') ) as P2) as T'
execute(#PivotBase)

Dynamic Pivot Columns in SQL Server

I have a table named Property with following columns in SQL Server:
Id Name
There are some property in this table that certain object in other table should give value to it.
Id Object_Id Property_Id Value
I want to make a pivot table like below that has one column for each property I've declared in 1'st table:
Object_Id Property1 Property2 Property3 ...
I want to know how can I get columns of pivot dynamically from table. Because the rows in 1'st table will change.
Something like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(Name)
FROM property
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query =
'SELECT *
FROM
(
SELECT
o.object_id,
p.Name,
o.value
FROM propertyObjects AS o
INNER JOIN property AS p ON o.Property_Id = p.Id
) AS t
PIVOT
(
MAX(value)
FOR Name IN( ' + #cols + ' )' +
' ) AS p ; ';
execute(#query);
SQL Fiddle Demo.
This will give you something like this:
| OBJECT_ID | PROPERTY1 | PROPERTY2 | PROPERTY3 | PROPERTY4 |
-------------------------------------------------------------
| 1 | ee | fd | fdf | ewre |
| 2 | dsd | sss | dfew | dff |