Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/sql-server-2008/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从3个表中透视2列并创建透视列名以避免冲突-SQL Server 2008R2 介绍与问题_Sql_Sql Server 2008_Pivot - Fatal编程技术网

从3个表中透视2列并创建透视列名以避免冲突-SQL Server 2008R2 介绍与问题

从3个表中透视2列并创建透视列名以避免冲突-SQL Server 2008R2 介绍与问题,sql,sql-server-2008,pivot,Sql,Sql Server 2008,Pivot,在我的例子中,我有老师、学生和课程。我想了解一下,哪门课程是由谁在哪间教室里教授的,以及这门课程的所有学生。我已经运行了基本设置(带有一些手工编码的语句)。但直到现在,我还没有准备好正确的材料声明: 准备@colsstudens,这样我就可以把名字放在列标题中,不必再弄乱id(添加100),以免rooms.id和students.id之间发生冲突 准备@colsRooms,这样我就不必硬拼房间名了 通过使用EXEC sp_executesql@sql将i全部放在一起 您可以找到所有sql语句

在我的例子中,我有老师、学生和课程。我想了解一下,哪门课程是由谁在哪间教室里教授的,以及这门课程的所有学生。我已经运行了基本设置(带有一些手工编码的语句)。但直到现在,我还没有准备好正确的材料声明:

  • 准备
    @colsstudens
    ,这样我就可以把名字放在列标题中,不必再弄乱id(添加100),以免rooms.id和students.id之间发生冲突
  • 准备
    @colsRooms
    ,这样我就不必硬拼房间名了
  • 通过使用
    EXEC sp_executesql@sql将i全部放在一起
您可以找到所有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    |   
到目前为止我所拥有的

缺少什么 上述声明是手工编写的。因为我事先不知道房间或学生,所以我需要动态地为房间和学生列创建Pivot语句。关于如何做到这一点,有很多例子。通常的做法是使用东西:

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                    
这将返回学生ID的
[101]、[102]、[103]、[104]
。我为每个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
但是我对stuff语句的所有尝试都失败了

用于创建表和数据的所有SQL语句
您可以使用动态sql查询为两个数据透视列创建别名字符串, 例如,对于学生专栏:

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查询为两个数据透视列创建别名字符串, 例如,对于学生专栏:

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)

我个人会做一些不同的事情。因为您正试图旋转两个单独的列,它们会尖叫着使用
UNPIVOT
函数

unpivot会将多个列转换为行,然后进行透视

由于您有SQL Server 2008,因此可以使用
交叉应用
和值:

  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)
看。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 |
您将看到,
col
数据包含要透视的所有值。一旦数据进入行中,if将很容易应用一个轴:

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

然后,若要将其转换为动态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,'')
这将得到在透视图中使用的不同房间和学生的列表。因此,最终代码将是:

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)

注意:我实现了一个用于透视的标志,如果房间或学生有值,这基本上会生成一个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 |        |       |     |

作为旁注,还可以使用sql server中的
unpivot
函数取消导入此数据。(参见)

我个人会做一些不同的事情。因为您正试图旋转两个单独的列,它们会尖叫着使用
UNPIVOT
函数

unpivot会将多个列转换为行,然后进行透视

由于您有SQL Server 2008,因此可以使用
交叉应用
和值:

  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)
看。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 |
您将看到,
col
数据包含要透视的所有值。一旦数据进入行中,if将很容易应用一个轴:

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

然后,若要将其转换为动态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,'')
这将得到在透视图中使用的不同房间和学生的列表。因此,最终代码将是:

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)

注意:我实现了一个用于透视的标志,如果房间或学生有值,这基本上会生成一个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 |        |       |     |

作为旁注,还可以使用sql server中的
unpivot
函数取消导入此数据。(请参阅)

我将更深入地研究上面的两个答案,并将它们与下面的答案进行比较

  • 我的问题是用
    Stuff()
    函数填充局部变量@RoomNames和@StudentNames。一个原因是我选择了数据类型
    nchar(120)
    ,而不是
    nvarchar(120)
    列的
    StudentName
    RoomName
  • 我遇到的另一个问题是,新的列名(Student而不是StudentName)没有被识别;因此,我在这条语句中将它们替换为
    *
    Select*From('+@PivotSrc+N')src
建议使用
SELECT@RoomIds=isnull(@RoomIds+,“,”)+'['+Cast(Id为nvarchar(20))+']',而不是
STUFF()
,因为我发现它更短,更容易阅读,所以我现在使用它

工作溶液
我将更深入地研究上面的两个答案,并将它们与下面的答案进行比较

  • 我的问题是用
    Stuff()
    函数填充局部变量@RoomNames和@StudentNames。一个原因是我选择了数据类型
    nchar(120)
    ,而不是
    nvarchar(120)
    列的
    StudentName
    RoomName
  • 我遇到的另一个问题是,新的列名(Student而不是StudentName)没有被识别;因此,我在这条语句中将它们替换为
    *
    Select*From('+@PivotSrc+N')src
建议使用
SELECT@RoomIds=isnull(@RoomIds+,”,“)+'['+Cast(Id为nvarchar(20))+']',而不是
STUFF()