【问题标题】:pivot to avoid numerous joins in TSQL?枢轴以避免SQL中的大量连接?
【发布时间】:2017-12-26 04:34:11
【问题描述】:

我有下表:

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,teacher) 是这个表的主键。

我想生成这样的结果

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')

【问题讨论】:

  • 创建两个枢轴然后将它们连接在一起?

标签: sql tsql pivot sql-server-2014


【解决方案1】:

无需创建两个 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

退货

生成的 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

子查询,它“FEEDS” PIVOT 生成以下内容

【讨论】:

    【解决方案2】:

    我还是更喜欢优秀的MAX(CASE)(由PIVOT在后台创建),很多剪切&粘贴&修改,但效率很高。

    看马,没有加入

    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】:

      根据您的采样日期

      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
      

      【讨论】:

        【解决方案4】:

        如果您有多个学生-教师的成绩记录。我将添加涵盖该问题的解决方案。它基于@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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-12-06
          • 2016-12-26
          • 2020-04-28
          • 1970-01-01
          • 2018-03-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多