pivot以避免TSQL中的大量连接?

时间:2017-07-20 12:54:24

标签: sql tsql pivot sql-server-2014

我有下表:

student teacher grade   gradedate
--------------------------------------
1       ALICE   A       05.08.2016
1       BOB     A       25.01.2015
1       CHARLES C       12.05.2017
1       DAVID   B       25.09.2013
2       BOB     D       01.02.2014
2       CHARLES A       26.04.2016
2       DAVID   C       02.05.2016

(学生,老师)是本表的主键。

我想生成像这样的结果

student ALICEGrade  ALICEGradeDate  BOBGrade    BOBGradeDate    CHARLESGrade    CHARLESGradeDate    DAVIDGrade  DAVIDGradeDate
-----------------------------------------------------------------------------------------------------------------------------------------------------------
1       A           05.08.2016      A           25.01.2015      C               12.05.2017          B           25.09.2013
2       NULL        NULL            D           01.02.2014      A               26.04.2016          C           02.05.2016

我设法通过为每位老师使用join子句来制作它:

SELECT st.student, 
a.grade as [ALICEGrade], a.gradedate as [ALICEGradeDate], 
b.grade as [BOBGrade], b.gradedate as [BOBGradeDate],
c.grade as [CHARLESGrade], c.gradedate as [CHARLESGradeDate],
d.grade as [DAVIDGrade], d.gradedate as [DAVIDGradeDate] 
FROM
(SELECT distinct [student] FROM [dbo].[TESTGRADETABLE]) st
LEFT join [dbo].[TESTGRADETABLE] a on a.teacher = 'ALICE' and a.student = st.student 
LEFT join [dbo].[TESTGRADETABLE] b on b.teacher = 'BOB' and b.student = st.student 
LEFT join [dbo].[TESTGRADETABLE] c on c.teacher = 'CHARLES' and c.student = st.student
LEFT join [dbo].[TESTGRADETABLE] d on d.teacher = 'DAVID' and d.student = st.student

但我想知道是否还有另一个更优雅的解决方案来避免众多连接(真正的请求有大约10个连接)。我想从以下开始使用枢轴:

SELECT * FROM [dbo].[TESTGRADETABLE] 
pivot
(
    max(grade)
    for  teacher in ([ALICE],[BOB],[CHARLES],[DAVE])
) piv1

但我被困在这里。我不知道是否可以用它生成TeacherGradeDate列。

用于创建表和数据的TSQL:

CREATE TABLE [dbo].[TESTGRADETABLE](
    [student] [int] NOT NULL,
    [teacher] [varchar](50) NOT NULL,
    [grade] [char](1) NOT NULL,
    [gradedate] [date] NOT NULL,
 CONSTRAINT [PK_TESTGRADETABLE] PRIMARY KEY CLUSTERED 
(
    [student] ASC,
    [teacher] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

INSERT INTO dbo.[TESTRATINGTABLE]
           ([student]
           ,[teacher]
           ,[grade]
           ,[gradedate])
     VALUES
           (1,'ALICE','A','2016-08-05'),
           (1,'BOB','A','2015-01-25'),
           (1,'CHARLES','C','2017-05-12'),
           (1,'DAVID','B','2013-09-25'),           
           (2,'BOB','D','2014-02-01'),
           (2,'CHARLES','A','2016-04-26'),
           (2,'DAVID','C','2016-05-02')

4 个答案:

答案 0 :(得分:3)

无需创建两个Pivots。可以通过Dynamic Pivot实现所需的结果。

示例

Declare @SQL varchar(max) = '
Select *
 From (
        Select B.*
         From  YourTable A
         Cross Apply (values (student,teacher+''Grade'',cast(grade as varchar(max)))
                            ,(student,teacher+''GradeDate'' ,cast(gradedate as varchar(max)))
                     ) B (student,item,value)
      ) A
 Pivot (max([Value]) For [Item] in (' + Stuff((Select Distinct ','+QuoteName(concat(teacher,'Grade')) 
                                                              +','+QuoteName(concat(teacher ,'GradeDate')) 
                                               From YourTable  
                                               Order By 1 
                                               For XML Path('')),1,1,'')  + ') ) p'
Exec(@SQL);
--Print @SQL

<强>返回

enter image description here

生成的SQL看起来像这样:

Select *
 From (
        Select B.*
         From  YourTable A
         Cross Apply (values (student,teacher+'Grade',cast(grade as varchar(max)))
                            ,(student,teacher+'GradeDate' ,cast(gradedate as varchar(max)))
                     ) B (student,item,value)
      ) A
 Pivot (max([Value]) For [Item] in ([ALICEGrade],[ALICEGradeDate],[BOBGrade],[BOBGradeDate],[CHARLESGrade],[CHARLESGradeDate],[DAVIDGrade],[DAVIDGradeDate]) ) p

“查询”PIVOT的子查询生成以下内容

enter image description here

答案 1 :(得分:2)

基于您的样本日期

DECLARE @Table1 TABLE 
    (student int, teacher varchar(7), grade varchar(1), gradedate varchar(10))
;

INSERT INTO @Table1
    (student, teacher, grade, gradedate)
VALUES
    (1, 'ALICE', 'A', '05.08.2016'),
    (1, 'BOB', 'A', '25.01.2015'),
    (1, 'CHARLES', 'C', '12.05.2017'),
    (1, 'DAVID', 'B', '25.09.2013'),
    (2, 'BOB', 'D', '01.02.2014'),
    (2, 'CHARLES', 'A', '26.04.2016'),
    (2, 'DAVID', 'C', '02.05.2016')
;

脚本:

;WITH CTE AS (
select student, teacher,col,val,Col1,val1
from  @Table1
CROSS APPLY (VALUES ('grade',grade))CS(COL,VAL) 
CROSS APPLY (VALUES ('gradedate',gradedate))CSS(COL1,VAL1)
)

Select T.student,
    MAX(T.ALICE) AS Alicegrade,
    MAX(TT.ALICE) AS AliceDate,
    MAX(T.BOB) As BobGrade,
    MAX(TT.BOB) As BobDate,
    MAX(T.CHARLES) AS CharlesGrade,
    MAX(TT.CHARLES) As CharlesDate,
    MAX(T.DAVID) As DavidGrade,
    MAX(TT.DAVID) As DavidDate  
        from  (
Select Student,
        [ALICE],
        [BOB],
        [CHARLES],
        [DAVID] 
            from  CTE 
            PIVOT (MAX(VAL)
                     for teacher in ([ALICE],[BOB],[CHARLES],[DAVID]))PVT )T
INNER JOIN 
(Select Student,
        [ALICE],
        [BOB],
        [CHARLES],
        [DAVID]
         from  CTE 
            PIVOT (MAX(VAL1) for teacher in ([ALICE],[BOB],[CHARLES],[DAVID]))PVT)TT
                    ON T.student = TT.student
                    GROUP BY T.student

答案 2 :(得分:2)

我还是喜欢好的&ol; MAX(CASE)(由PIVOT在后​​台创建),大量剪切和粘贴修改,但效率很高。

看Ma,没有加入

select student, 
   max(case when teacher = 'ALICE' then grade end)       AS ALICEGrade,
   max(case when teacher = 'ALICE' then gradedate end)   AS ALICEGradeDate,
   max(case when teacher = 'BOB' then grade end)         AS BOBGrade,
   max(case when teacher = 'BOB' then gradedate end)     AS BOBGradeDate,
   max(case when teacher = 'CHARLES' then grade end)     AS CHARLESGrade,
   max(case when teacher = 'CHARLES' then gradedate end) AS CHARLESGradeDate,
   max(case when teacher = 'DAVID' then grade end)       AS DAVIDGrade,
   max(case when teacher = 'DAVID' then gradedate end)   AS DAVIDGradeDate
from TESTGRADETABLE
group by student

答案 3 :(得分:1)

如果您有一个以上的学生教师成绩记录。我将添加解决方案。它基于@dnoeth解决方案。

SELECT student,
   max(case when teacher = 'ALICE' then grade end)       AS ALICEGrade,
   max(case when teacher = 'ALICE' then gradedate end)   AS ALICEGradeDate,
   max(case when teacher = 'BOB' then grade end)         AS BOBGrade,
   max(case when teacher = 'BOB' then gradedate end)     AS BOBGradeDate,
   max(case when teacher = 'CHARLES' then grade end)     AS CHARLESGrade,
   max(case when teacher = 'CHARLES' then gradedate end) AS CHARLESGradeDate,
   max(case when teacher = 'DAVID' then grade end)       AS DAVIDGrade,
   max(case when teacher = 'DAVID' then gradedate end)   AS DAVIDGradeDate

FROM (
SELECT student, 
       teacher, 
       grade, 
       gradedate, 
       ROW_NUMBER() OVER(PARTITION BY teacher, student ORDER BY grade asc, gradedate desc) as ord
FROM testgradetable
) grades
WHERE ord = 1
GROUP BY student