使用来自不同表的动态列创建查询

时间:2013-01-17 12:01:52

标签: sql-server tsql pivot

我正在尝试根据来自三个表的数据创建一个包含动态列的查询。

这是数据库结构:

学生

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

EXAM:

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

EXAM_REGISTRATION:

  • studentID int
  • examID int

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

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

enter image description here

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

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

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

有人可以帮助我吗?

2 个答案:

答案 0 :(得分: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中连接字符串来生成列列表和studentID列表

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的信息 如果你想阅读关于动态去this link

编辑:要根据评论调整问题的查询,你必须更改@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

答案 1 :(得分:0)

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

静态版本将是:

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 with Demo

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

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 with Demo

结果将是:

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