将行数据组合并转换为列

时间:2018-02-09 08:00:10

标签: sql sql-server tsql pivot

我有一个SQL数据库表,看起来像这样

Person  | Color
Alex    | red
Alex    | blue
Alex    | orange
Mike    | green
Tom     | blue
Tom     | black

我需要制作这样的副本

Person | Color_1| Color_2| Color_3
Alex   | red    | blue   | orange
Mike   | green
Tom    | blue  | black

2 个答案:

答案 0 :(得分:1)

我们需要动态地执行此操作。我们需要获得我们将要拥有的列总数。我们需要为PIVOT创建一个动态T-SQL。

试试这个:

CREATE TABLE #DataSource
(
    [name] VARCHAR(12)
   ,[color] VARCHAR(12)
);

INSERT INTO #DataSource ([name], [color])
VALUES ('Alex', 'red')
      ,('Alex', 'blue')
      ,('Alex', 'orange')
      ,('Mike', 'green')
      ,('Tom ', 'blue')
      ,('Tom ', 'black');

DECLARE @DynammicTSQLStatement NVARCHAR(MAX)
       ,@DynamicPIVOTColumns NVARCHAR(MAX);

DECLARE @MaxNumberOfColorsPerName INT;

WITH DataSource AS
(
    SELECT COUNT([color]) OVER (PARTITION BY [name]) AS [ColorsCount]
    FROM #DataSource
)
SELECT @MaxNumberOfColorsPerName = MAX([ColorsCount])
FROM DataSource;

SET @DynamicPIVOTColumns = STUFF
                          (
                                (
                                    SELECT TOP (@MaxNumberOfColorsPerName) ',Color_' +  CAST(ROW_NUMBER() OVER(ORDER BY t1.number) AS VARCHAR(12)) AS N
                                    FROM master..spt_values t1 
                                    CROSS JOIN master..spt_values t2                             
                                    ORDER BY ROW_NUMBER() OVER (ORDER BY t1.number)
                                FOR XML PATH('') ,TYPE
                                ).value('.', 'NVARCHAR(MAX)')
                                ,1
                                ,1
                                ,''
                          );

SET @DynammicTSQLStatement = N'
SELECT *
FROM 
(
    SELECT *
          ,''Color_'' + CAST(ROW_NUMBER() OVER (PARTITION BY [name] ORDER BY [color]) AS VARCHAR(12)) AS [color_number]
    FROM #DataSource
) DS
PIVOT
(
    MAX([color]) FOR [color_number] IN (' + @DynamicPIVOTColumns + ')
) PVT';

EXEC sp_executesql @DynammicTSQLStatement;

DROP TABLE #DataSource;

enter image description here

答案 1 :(得分:0)

首先我将数据操作为所需的格式

IF OBJECT_ID('tempdb..##Getdata')IS NOT NULL
DROP TABLE ##Getdata

;With cte(Person, Color)
AS
(
SELECT 'Alex'    , 'red'    UNION ALL
SELECT 'Alex'    , 'blue'   UNION ALL
SELECT 'Alex'    , 'orange' UNION ALL
SELECT 'Mike'    , 'green'  UNION ALL
SELECT 'Tom'     , 'blue'   UNION ALL
SELECT 'Tom'     , 'black' 
)
,Cte_Final
AS
(
SELECT DENSE_RANK()OVER(ORDER BY Person )AS Rnk
      ,Person
      ,Color 
      ,'Color_'+CAST(DENSE_RANK()OVER(ORDER BY Person ) AS VARCHAR(2)) AS ColrCol
 FROM cte
)
SELECT DISTINCT Rnk
        ,Person
        ,ColrCol
        ,STUFF((SELECT DISTINCT  ', '+Color 
                    FROM Cte_Final i WHERE i.Rnk=o.Rnk
                 FOR XML PATH ('')),1,1,'') AS
                  Color
    INTO ##Getdata
FROM Cte_Final o

使用Dynamic Sql我得到了预期的结果

DECLARE @COlumn nvarchar(max),@Sql nvarchar(max)

SELECT  @COlumn=STUFF((SELECT DISTINCT  ', '+'Split.a.value(''/S['+CAST(Rnk AS VARCHAR(2))+']'''+','+'''nvarchar(100)'''+') As '  + QUOTENAME(ColrCol )
                    FROM ##Getdata i
                 FOR XML PATH ('')),1,1,'') 

SET @Sql='SELECT DISTINCT Person,'+@COlumn+' FROM
            (
            SELECT Person, 
            CAST(''<S>''+REPLACE (Color,'','',''</S><S>'')+''</S>'' AS XML ) AS Color
            FROM ##Getdata
            ) AS A
            CROSS APPLY Color.nodes(''S'') AS Split(a)
            '
PRINT @Sql
EXEC(@Sql)

结果

Person  Color_1 Color_2  Color_3
--------------------------------
Alex     blue    orange  red
Mike     green   NULL    NULL
Tom      black   blue    NULL

从以下链接查看演示 Expected Result for your data