2

我正在尝试根据三个表中的数据创建一个带有动态列的查询。

这是数据库结构:

学生

  • studentID int,
  • studentNumber int,
  • studentName nvarchar(100).

考试:

  • examID int,
  • examName varchar(100),
  • examenDate datetime,

考试注册:

  • studentID int,
  • examID int,

EXAM_REGISTRATION当学生注册考试时,表格中会添加一条记录。

我想要得到的是一个数据透视表中所有考试和所有学生的列表,以查看哪些学生已经注册了哪些考试,如下所示:

在此处输入图像描述

坦率地说,我不知道从哪里开始。

我可以单独查询所有内容并将它们放在一起,但是如何将它们组合成一个查询?

我一直在研究数据透视表,但每个示例似乎只从一个表中查询,并使用数字和函数MINAVG

有人可以帮我吗?

4

2 回答 2

1

好的,我们走吧

一些数据可以玩

create table #student
(studentID int, studentNumber int, studentName nvarchar(100))
create table #exam 
(examID int, examName nvarchar(100), examDate datetime)
create table #examReg
(studentID int, examID int)

insert into #student
values (1, 787878, 'pierwszy')
,(2, 89898, 'drugi')
,(3, 343434, 'trzeci')
,(4, 121212, 'czwarty')

insert into #exam
values (1, 'exPierwszy', GETDATE())
,(2, 'exDrugi', GETDATE())
,(3, 'exTrzeci', GETDATE())

insert into #examReg
values (1,2),(1,3)
, (2,2),(2,3)
,(3,1),(3,2)
,(4,1),(4,2),(4,3)

现在是主要部分和解释

首先,您必须获得枢轴查询

select examName, examDate , min([1])  , min([2]), min([3]) ,min([4])--studentID as studentID, examDate --,studentNumber
from 
(select a.studentID , studentNumber, examDate, examName
from #student a 
join #examReg b on a.studentID = b.studentID
join #exam c on c.examID = b.examID ) as m
pivot
(min(studentNumber) FOR studentID in ([1],[2],[3],[4])) as t
group by examName, examDate

正如你所拥有的,只需在数据透视声明中更改它的 select 语句和 studentID 列表,你必须动态生成这些部分,所以我们只需复制先前编写的查询并用我们的令牌替换列

    declare @sqlTemplate nvarchar(max) = 
    'select examName, examDate @@sqlColumnList@@
    from 
    (select a.studentID , studentNumber, examDate, examName
    from #student a 
    join #examReg b on a.studentID = b.studentID
    join #exam c on c.examID = b.examID ) as m
    pivot
    (min(studentNumber) FOR studentID in (@@sqlStudentIDList@@)) as t
    group by examName, examDate
'

之后,您通过在 tsql 中连接字符串来生成列列表和学生 ID 列表

declare @sqlColumnList nvarchar(max) = ''
select @sqlColumnList += ',min([' + cast(studentID as nvarchar(10)) + ']) as [' + studentName +'(' + cast(studentNumber as nvarchar(10)) + ')]'
from #student
declare @sqlStudentIDList nvarchar(max) = ''
select @sqlStudentIDList += '[' + CAST(studentID as nvarchar(10)) + '],'
from #student

set @sqlStudentIDList = SUBSTRING(@sqlStudentIDList, 0, LEN(@sqlStudentIDList))
select @sqlStudentIDList

一旦你有了它,你所要做的就是替换以前模板中的标记

set @sqlTemplate = REPLACE(@sqlTemplate, '@@sqlColumnList@@', @sqlColumnList)
set @sqlTemplate = REPLACE(@sqlTemplate, '@@sqlStudentIDList@@', @sqlStudentIDList)


select @sqlTemplate

exec sp_executesql @sqlTemplate

如果你想阅读更多关于 pivot go for msdn 的信息,那就是它,如果你想阅读这个链接的动态 go

编辑:要从评论中调整问题的查询,您必须像这样更改@sqlColumnList

select @sqlColumnList += ',min(' + QUOTENAME(studentID)  + ') as Student' + CAST(studentID as nvarchar(10))  + '_REG,
'''+ studentName + ''' as Student' + cast(studentID as nvarchar(10)) + '_NAME,
'+ cast(studentID as nvarchar(10)) + ' as Student' + cast(studentID as nvarchar(10)) + '_ID'
from #student
于 2013-01-17T13:06:15.373 回答
0

这是数据的枢纽。我会执行此操作与其他答案略有不同。如果您知道所有值,则可以对这些值进行硬编码。

静态版本将是:

select examname,
  examendate,
  IsNull([Smith, John (14323)], 'false') [Smith, John (14323)],
  IsNull([Craft, Peter (14334)], 'false') [Craft, Peter (14334)],
  IsNull([Davis, Alan (13432)], 'false') [Davis, Alan (13432)],
  IsNull([Newman, Ted (133123)], 'false') [Newman, Ted (133123)]
from
(
  select e.examname,
    e.examenDate,
    s.studentName + ' ('+cast(s.studentnumber as varchar(50))+')' studentNameNum,
    'true ' as Flag
  from exam e
  left join exam_registration er
    on e.examid = er.examid
  right join student s
    on er.studentid = s.studentid
) src
pivot
(
  max(flag)
  for studentNameNum in ([Smith, John (14323)], [Craft, Peter (14334)],
                      [Davis, Alan (13432)], [Newman, Ted (133123)])
) piv

请参阅带有演示的 SQL Fiddle

如果您的值未知,则查询将是:

DECLARE @cols AS NVARCHAR(MAX),
    @colsNull AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(s.studentName + ' ('+cast(s.studentnumber as varchar(50))+')') 
                    from student s
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @colsNull = STUFF((SELECT distinct ',IsNull(' + QUOTENAME(s.studentName + ' ('+cast(s.studentnumber as varchar(50))+')')+', ''false'')'+' as '+QUOTENAME(s.studentName+' ('+cast(s.studentnumber as varchar(50))+')') 
                    from student s
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT examname,
                  examenDate,' + @colsNull + ' from 
             (
                select e.examname,
                  e.examenDate,
                  s.studentName + '' (''+cast(s.studentnumber as varchar(50))+'')'' studentNameNum,
                  ''true '' as Flag
                from exam e
                left join exam_registration er
                  on e.examid = er.examid
                right join student s
                  on er.studentid = s.studentid
            ) x
            pivot 
            (
                max(flag)
                for studentNameNum in (' + @cols + ')
            ) p '

execute(@query)

请参阅带有演示的 SQL Fiddle

结果将是:

| EXAMNAME |          EXAMENDATE | CRAFT, PETER (14334) | DAVIS, ALAN (13432) | NEWMAN, TED (133123) | SMITH, JOHN (14323) |
----------------------------------------------------------------------------------------------------------------------------
|   Exam 1 | 2013-01-01 12:00:00 |                false |               false |                true  |               false |
|   Exam 2 | 2013-01-01 14:00:00 |                true  |               false |                false |               true  |
|   Exam 3 | 2013-01-02 12:00:00 |                true  |               true  |                false |               false |
|   Exam 4 | 2013-01-02 14:00:00 |                false |               false |                true  |               false |
|   Exam 5 | 2013-01-03 12:00:00 |                false |               false |                false |               true  |
于 2013-01-17T15:23:53.273 回答