sur*_*gle 1 sql pivot sql-server-2008
在我的例子中,我有老师,学生和课程.我想概述哪个课程由谁在本课程的哪些房间和所有学生中进行教学.我有基本的设置runnig(带有一些手动编码的语句).但直到现在我没有运气准备正确的STUFF声明:
@colsStudents以便我可以将名称放在列标题中,并删除混乱ID(添加100)的需要,以避免在rooms.id和students.id之间发生冲突@colsRooms这样我就不必为房间名称加油EXEC sp_executesql @sql;您可以找到所有sql语句来创建此架构和最后的数据.

我想转动列RoomName,StudentName并使用列值作为新列名称.所有用于创建表和数据的SQL语句都在最后.
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 |
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

以上陈述是手工准备的.由于我不提前知道房间或学生,我需要动态创建列房间和学生的透视声明.在SO上有很多例子如何做到这一点.通常的方法是使用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
Run Code Online (Sandbox Code Playgroud)
这将返回[101],[102],[103],[104]学生ID.我为每个id添加了100,以避免students.id和teh rooms.id列之间发生冲突.
正如介绍中提到的,我需要动态创建这样的东西
[1] as RoomName_1, [2] as RoomName_1 -- RoomColumns
[1] as StudentName1, [2] as StudentName2, ... ,[4] as Amy -- StudentColumns
Run Code Online (Sandbox Code Playgroud)
但我所有尝试使用stuff语句都失败了.
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)
Run Code Online (Sandbox Code Playgroud)
我个人会这样做有点不同.因为你试图转动两个尖叫的单独列来使用该UNPIVOT函数.
unpivot会将您的多个列转换为行然后进行透视.
由于您有SQL Server 2008,因此可以使用CROSS APPLY和值:
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)
Run Code Online (Sandbox Code Playgroud)
见演示.unpivot生成类似于此的结果:
| 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 |
Run Code Online (Sandbox Code Playgroud)
您将看到col数据包含您要转动的所有值.一旦数据在行中,if将很容易应用一个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
Run Code Online (Sandbox Code Playgroud)
然后,要将其转换为动态SQL,您只能旋转一列,因此您将使用以下内容来获取列列表:
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,'')
Run Code Online (Sandbox Code Playgroud)
这将获得在枢轴中使用的不同房间和学生的列表.所以最终的代码是:
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)
Run Code Online (Sandbox Code Playgroud)
注意我实现了一个在枢轴中使用的标志,如果房间或学生有值,这基本上会产生一个Y/N.
这给出了最终结果:
| 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 | | | |
Run Code Online (Sandbox Code Playgroud)
另请注意,此数据也可以使用unpivotsql server中的函数进行转换.(参见使用unpivot演示)
| 归档时间: |
|
| 查看次数: |
1365 次 |
| 最近记录: |