将表从行转换为列

时间:2015-10-12 14:41:04

标签: sql-server tsql

我收到了以下格式的有序Microsoft Server表:

    Name   Product
  ------------------
1 | Mayer  Product_1
2 | Mayer  Product_1
3 | Mayer  Product_2

我想获得以下结果:

    Name   Purchase_1 Purchase_2 Purchase_3
  -----------------------------------------
1 | Mayer  Product_1  Product_1  Product_2

代码必须适用于任意长度的购买和名称,这意味着我不能提前了解这些信息。

1 个答案:

答案 0 :(得分:3)

动态PIVOT 是您的朋友:

LiveDEMO

CREATE TABLE #mytable(
   Name  VARCHAR(80) NOT NULL 
  ,Product VARCHAR(160) NOT NULL
);
INSERT INTO #mytable VALUES ('Mayer','Product_1');
INSERT INTO #mytable VALUES ('Mayer','Product_1');
INSERT INTO #mytable VALUES ('Mayer','Product_2');
INSERT INTO #mytable VALUES ('Kowalsky','Product_1');
INSERT INTO #mytable VALUES ('Kowalsky','Product_2');
INSERT INTO #mytable VALUES ('Kowalsky','Product_3');
INSERT INTO #mytable VALUES ('Kowalsky','Product_4');

DECLARE @cols  NVARCHAR(MAX),
        @cols_piv NVARCHAR(MAX),
        @query NVARCHAR(MAX)
        ,@max  INT = 0;

SELECT @max = MAX(c)
FROM (
  SELECT Name, COUNT(Product) AS c
  FROM #mytable
  GROUP BY Name) AS s;

SET @cols = STUFF(     
            (SELECT ',' +  CONCAT('[',c.n, '] AS Purchase_',c.n, ' ')
            FROM  ( SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects ORDER BY n)AS c(n)
            WHERE c.n <= @max
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'');

SET @cols_piv = STUFF(
            (SELECT ',' +  CONCAT('[',c.n, '] ')
            FROM  ( SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects ORDER BY n)AS c(n)
            WHERE c.n <= @max
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'');        

SET @query = N'SELECT Name, ' + @cols + ' from 
            (
                select Name, Product,
                [rn] = ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Product)
                from #mytable
            ) x
            pivot 
            (
                max(Product)
                for rn in (' + @cols_piv + ')
            ) p ';

-- SELECT @query; 

EXEC [dbo].[sp_executesql]
    @query;

起初可能很复杂,但这很简单。普通PIVOT要求您事先知道列列表。这不是您的选择,因此您需要生成列并使用Dynamic-SQL。

工作原理:

  1. @max包含每行的最大列数
  2. @cols包含带有别名的SELECT列列表
  3. @cols_piv包含数字列表[1], [2], ... @max
  4. 将其与普通PIVOT查询
  5. 连接
  6. 执行它并享受您的结果。
  7. 警告:

    • 我使用sys.objects作为我的号码生成器。您可以将其替换为您想要的(递归CTE /多步CTE /计数表...)。

    • 如果您使用的是SQL Server 2008,则需要将CONCAT替换为+