I want to spit a column into multiple columns based on the rank by partition or column type
Sample Input Data:
STUDENT PROGRAM_ID DEG_TYPE PROGRAM_RN
1 Program1.MA MA 1
1 Program2.MA MA 1
2 Program1.PHD DOC 1
2 Program2.MA MA 2
3 Program.CERT CERT 3
3 Program1.PSYD DOC 1
3 Program2.MA MA 2
Expected Output
Student Highest Program Second Highest Program Third Highest Program
1 Program1.MA, Program2.MA
2 Program1.PSYD Program2.MA
3 Program1.PHD Program2.MA Program.CERT
I have tried using PIVOT and I am able to split the column but with that I am able to fetch only one program as Highest, second and so on. My requirement is to get all the Programs that has RN 1 listed for each student in highest program, all with RN 2 listed in Second and so on.
**CODE**
SELECT [STUDENTS_ID],[1], [2], [3]
FROM
(SELECT [STUDENTS_ID]
,[PROGRAM_ID]
,[PROGRAM_RN]
FROM [dbo].[CTE] ) AS SourceTable
PIVOT
(
MAX([PROGRAM_ID])
FOR [PROGRAM_RN] IN ([1], [2], [3])
) As PivotTable;
Current Output
Student Highest Program Second Highest Program Third Highest Program
1 Program1.MA
2 Program1.PSYD Program2.MA
3 Program1.PHD Program2.MA Program.CERT
I also want to know if there is a better way of doing this
If you are using SQL 2017 or higher version, you can use string_agg function instead of XML PATH in below query else you can use the same query to dynamically achieve the result set you want.
DECLARE @Columns AS NVARCHAR(MAX),
@Query AS NVARCHAR(MAX)
SET @Columns = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.PROGRAM_RN)
FROM StudentTable c
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET @Query = 'SELECT STUDENT, ' + @Columns + ' FROM
(
SELECT DISTINCT ST2.STUDENT ,PROGRAM_RN,
SUBSTRING(
(
SELECT '', ''+ST1.PROGRAM_ID AS [text()]
FROM dbo.StudentTable ST1
WHERE ST1.STUDENT = ST2.STUDENT AND ST1.PROGRAM_RN = ST2.PROGRAM_RN
ORDER BY ST1.STUDENT
FOR XML PATH ('''')
), 2, 1000) [PROGRAM_ID]
FROM dbo.StudentTable ST2
) x
PIVOT
(
MAX(PROGRAM_ID)
FOR PROGRAM_RN IN (' + @Columns + ')
) p '
EXEC(@Query)
Sort of odd that you have ties. But you can use conditional aggregation with strings:
select student,
string_agg(case when program_rn = 1 then program_id end, ', '),
string_agg(case when program_rn = 2 then program_id end, ', '),
string_agg(case when program_rn = 3 then program_id end, ', ')
from t
group by student;
If you know the maximum that need to be concatenated, you can use conditional aggregation:
select student,
concat(max(case when program_rn = 1 and seqnum = 1 then program_id + '; ' end),
max(case when program_rn = 1 and seqnum = 2 then program_id + '; ' end),
max(case when program_rn = 1 and seqnum = 3 then program_id + '; ' end)
),
concat(max(case when program_rn = 2 and seqnum = 1 then program_id + '; ' end),
max(case when program_rn = 2 and seqnum = 2 then program_id + '; ' end),
max(case when program_rn = 2 and seqnum = 3 then program_id + '; ' end)
),
concat(max(case when program_rn = 3 and seqnum = 1 then program_id + '; ' end),
max(case when program_rn = 3 and seqnum = 2 then program_id + '; ' end),
max(case when program_rn = 3 and seqnum = 3 then program_id + '; ' end)
),
from (select t.*,
row_number() over (partition by student, program_rn order by program_id) as seqnum
from t
) t
group by student;
This is cumbersome, but possibly simpler than FOR XML PATH
.
Note that I changed the delimiter to a semicolon, because that seems more natural for leaving it at the end of the list. Although it can be removed, that just further complicates the logic, perhaps unnecessarily.
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.