简体   繁体   中英

Split single column into multiple columns based on Rank Number

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.

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