Search code examples
sql-servert-sqlpivotsql-server-2014dynamic-pivot

Convert (text) rows into Columns in SQL Server 2014


I'm trying to capture rows for a specific group and then convert them into columns naming the script's headers.

This is an example of data:

FORMULAS  | INSTRUCTIONS
----------+----------------------------------------------------
AF100120  | **ACETALDEHYDE WARNING - USE APPROPRIATE CAUTION**
AF100120  | ADD REMAINING ITEMS   
AF100120  | DISSOLVE ITEMS 1-2 IN 3 
AF100120  | IF HEAT USED, COOL TO ROOM TEMPERATURE 
AF100120  | MIX UNTIL HOMOGENEOUS                                                                               
AF100120  | MIXING TIMES VARY WITH BATCH SIZE                                                                   
AF100997  | 1) DISSOLVE THYMOL CRYSTALS IN ETHYL ALCOHOL. MIX WELL.
AF100997  | 2) ADD REMAINING ITEMS AND MIX UNTIL UNIFORM.
AF100997  | 3) FILTER IF NOT CLEAR

The desired output for this script is:

FORMULAS | INSTRUCTION #1 |INSTRUCTION #2 | INSTRUCTION #3 |INSTRUCTION #4 |INSTRUCTION #5|INSTRUCTION #6

AF100120 |**ACETALDEHYDE..| ADD REMAINING.| DISSOLVE ITEM. |IF HEAT USED.. |MIX UNTIL H   |MIXING TIM...
AF100997 |1) DISSOLVE THYM| 2) ADD REMAINI| FILTER IF NOT  | NULL          |NULL          |NULL          

I'd appreciate if someone could help me with my code:

SELECT *
FROM
    (SELECT 
         A.FormulaId AS FORMULAS, 
         CAST(B.Instruction AS NVARCHAR(100)) AS [INSTRUCTIONS]
     FROM 
         Formulas AS A
     LEFT JOIN 
         FormulaProcedures AS B ON A.FormulaId = B.FormulaId
     WHERE 
         B.ProcedureType = 3 
         AND PhaseType = 2 
         AND YEAR(A.VersionDate) >= '2018' 
         AND A.PrimaryVersion = 1 
         AND A.FormulaId IN('AF100120', 'AF100997')
     GROUP BY  
         A.FormulaId, CAST(B.Instruction AS NVARCHAR(100))
    ) AS SourceTable 
PIVOT
    (MAX([INSTRUCTIONS]) 
         FOR [INSTRUCTIONS] IN ([INSTRUCTION #1], [INSTRUCTION #2],
                                [INSTRUCTION #3], [INSTRUCTION #4],
                                [INSTRUCTION #5], [INSTRUCTION #6])) AS PivotTable;

Thank you!


Solution

  • You might use Dynamic Pivot including ROW_NUMBER() function with values grouped by formulas ordered by instructions column in order to number the column names along with iteration of that analytic function :

    DECLARE @cols  AS NVARCHAR(MAX),  @query AS NVARCHAR(MAX)
    
    SELECT @cols = 
           STUFF((SELECT DISTINCT ',' + QUOTENAME(CONCAT('formulas',[rn])) AS formulas
                    FROM 
                    (
                     SELECT f.*, 
                            ROW_NUMBER() OVER 
                                     (PARTITION BY [formulas] ORDER BY [instructions]) AS rn
                       FROM [Formulas_Tab] f
                    ) ff
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'');
    
    SET  @query = 
     N'SELECT *  
         FROM
         (
           SELECT f.*, 
                  CONCAT(''formulas'',
                         ROW_NUMBER() OVER 
                                  (PARTITION BY [formulas] ORDER BY [instructions])) AS rn
             FROM [Formulas_Tab] f
          ) ff
        PIVOT 
        (
         MAX([instructions]) FOR [rn] IN (' + @cols + N')
        ) p '
    
    EXEC sp_executesql @query;
    

    Demo

    If your version of installed database was 2017+, then you'd use STRING_AGG() function in order to determine @cols variable as

    SELECT @cols =
    SELECT STRING_AGG(QUOTENAME(CONCAT('formulas', [ rn ])), ',') AS formulas
      FROM 
      (
       SELECT DISTINCT ROW_NUMBER() OVER
              ( PARTITION BY [ formulas ] ORDER BY [ instructions ]) AS rn
         FROM [ Formulas_Tab ] f
      ) ff