简体   繁体   中英

Should I use Pivot to translate SQL Server 2008 row values to column names when I don't want to use an aggregate?

I am trying to get a handle on manipulating table data into more visually appealing formats for output. This could be part of the problem as what I want may be intended for separate reporting software.

I have a table that looks like this

teacher    student
----------------------
teacher1   Bob
teacher1   Jim
teacher2   Sam
teacher3   Bill
teacher3   John
teacher3   Eric

I want a table that looks something like this:

teacher1    teacher2    teacher3
---------------------------------
Bob         Sam          Bill
Jim         null         John
null        null         Eric

So I tried stuffing all the teacher names in a variable and then using a Pivot but since I have to choose an aggregate I can only get the Max or Min student like this:

DECLARE @teacherList AS VARCHAR(max)

SELECT @teacherList = Stuff((SELECT DISTINCT',[' + teacher + ']'
                              FROM myTable
                              FOR xml path('')), 1, 1, '')

DECLARE @dynamic_pivot_query AS VARCHAR(max)

SET @dynamic_pivot_query = 'select' + @teacherList + 
'from 
(
    SELECT [teacher],[student]
    FROM [dbo].[myTable]
) as S
Pivot
(
    MIN([student])
    FOR teacher IN (' + @teacherList + ')
) as P
'
EXEC(@dynamic_pivot_query)  

The result of this is:

teacher1    teacher2    teacher3
---------------------------------
Bob         Sam          Bill

Assuming the following:

  1. # of teachers and their names are unknown (variable)
  2. # of students per teacher is unknown and likely different for every teacher

Is there a way to do this?

No.

SQL Server requires static typing. There is no way to create a dynamic number of columns or dynamic column types (except for sql_variant).

Therefore your dynamic SQL solution is the only possible choice.

Don't let min/max confuse you: There will always be exactly 0 or 1 item per aggregation. The syntax requires an aggregate for theoretical correctness, but if (teacher, student) is unique the aggregate is doing nothing. It does no harm and it does not alter the results.

The approach is right just the way it is. Actually, I am working on the same type of code right now in this minute (which is funny).

You can use row_number to get the result you want.

SET @dynamic_pivot_query = 'select ' + @teacherList + 
'from 
(
    SELECT [teacher],[student], row_number() over(partition by teacher order by student) as rn
    FROM [dbo].[myTable]
) as S
Pivot
(
    MIN([student])
    FOR teacher IN (' + @teacherList + ')
) as P
'

Update:
To remove the SQL Injection vulnerability you should use quotename to properly quote your field list.

SELECT @teacherList = Stuff((SELECT DISTINCT',' + quotename(teacher)
                              FROM myTable
                              FOR xml path('')), 1, 1, '') 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM