SQL:将一列的数据转换为多列

时间:2015-10-12 12:42:56

标签: sql sql-server tsql

我尝试使用像Pivot这样的东西寻找问题的解决方案,但它们似乎没有给出我正在查看的输出,因为它们似乎将一行中的特定值映射到列。

我有两列,第一列包含"键"字段和第二个正在改变数据。 e.g。

╔══════════╦══════════════════╗
║ Project  ║     Location     ║
╠══════════╬══════════════════╣
║ ProjectA ║ \\Server1\Share1 ║
║ ProjectA ║ \\Server2\Share1 ║
║ ProjectB ║ \\Server6\Share2 ║
║ ProjectB ║ \\Server1\Share2 ║
║ ProjectB ║ \\Server2\Share3 ║
║ ProjectC ║ \\Server8\Share2 ║
║ ProjectD ║ \\Server5\Share9 ║
║ ProjectD ║ \\ServerX\ShareY ║
╚══════════╩══════════════════╝

我想要实现的输出如下:

╔══════════╦══════════════════╦══════════════════╦══════════════════╦═════════╦══════════╦═════════╗
║ Project  ║     Column1      ║     Column2      ║     Column3      ║ Column4 ║ Column5  ║ ColumnX ║
╠══════════╬══════════════════╬══════════════════╬══════════════════╬═════════╬══════════╬═════════╣
║ ProjectA ║ \\Server1\Share1 ║ \\Server2\Share1 ║ NULL             ║ NULL    ║ NULL     ║         ║
║ ProjectB ║ \\Server1\Share2 ║ \\Server2\Share3 ║ \\Server6\Share2 ║ NULL    ║ NULL     ║         ║
║ ProjectC ║ \\Server8\Share2 ║ NULL             ║ NULL             ║ NULL    ║ NULL     ║         ║
║ ProjectD ║ \\Server5\Share9 ║ \\ServerX\ShareY ║ NULL             ║ NULL    ║ NULL     ║         ║
╚══════════╩══════════════════╩══════════════════╩══════════════════╩═════════╩══════════╩═════════╝

如果该列没有数据,那么它将为NULL。

位置列中的不同值的数量是动态的,所需的输出是通用列名称,在相应的项目值旁边具有不同的位置值。

希望有人可以帮助我解决这个问题,因为它让我发疯了!

提前致谢。

2 个答案:

答案 0 :(得分:1)

警告:

此解决方案假设它将是最多6列,您可以添加更多,例如最多20列。

<强> LiveDemo

数据:

CREATE TABLE #mytable(
   Project  VARCHAR(80) NOT NULL 
  ,Location VARCHAR(160) NOT NULL
);
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server1\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server2\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server6\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server1\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server2\Share3');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectC','\\Server8\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\Server5\Share9');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\ServerX\ShareY');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\ServerX\ShareY');

查询:

WITH cte AS
(
  SELECT Project, Location,
    [rn] = RANK() OVER (PARTITION BY Project ORDER BY Location)
  FROM #mytable
)
SELECT
   Project
  ,Column1 = MAX(CASE WHEN rn = 1 THEN Location ELSE NULL END)
  ,Column2 = MAX(CASE WHEN rn = 2 THEN Location ELSE NULL END)
  ,Column3 = MAX(CASE WHEN rn = 3 THEN Location ELSE NULL END)
  ,Column4 = MAX(CASE WHEN rn = 4 THEN Location ELSE NULL END)
  ,Column5 = MAX(CASE WHEN rn = 5 THEN Location ELSE NULL END)
  ,Column6 = MAX(CASE WHEN rn = 6 THEN Location ELSE NULL END)
  -- ....
  --  ,ColumnX = MAX(CASE WHEN rn = X THEN Location ELSE NULL END)
FROM cte
GROUP BY Project;

编辑:

使用Dynamic-SQL并生成Pivoted列列表的真正通用解决方案:

<强> LiveDemo2

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

SELECT @max = MAX(c)
FROM (
  SELECT Project, COUNT(DISTINCT Location) AS c
  FROM #mytable
  GROUP BY Project) AS s;


SET @cols = STUFF(     
            (SELECT ',' +  CONCAT('[',c.n, '] AS Column',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,'');        

-- SELECT @cols;

set @query = N'SELECT Project, ' + @cols + ' from 
            (
                select Project, Location,
                [rn] = RANK() OVER (PARTITION BY Project ORDER BY Location)
                from #mytable
            ) x
            pivot 
            (
                max(Location)
                for rn in (' + @cols_piv + ')
            ) p ';

-- SELECT @query; 

EXEC [dbo].[sp_executesql]
    @query;

答案 1 :(得分:0)

归功于@ lad2025。

我正在使用Server2005,所以这些是我为了让它适合我而必须做的修改:

  • 无法在同一行中声明和分配变量。 @max INT = 0;
  • CONCAT函数不存在,因此在参数之间用+替换。 ', ' + '[' + convert(varchar, c.n) + '] AS Column' + c.n
  • c.n属于bigint类型,因此我必须CONVERT将其VARCHAR - CONVERT(VARCHAR, c.n)
  • 生成要转动的表格时,需要提供GROUP BY,否则数据会显示在奇怪的列号中。

没有GROUP BY 如果没有GROUP BY,因为初始#mytable中的数据越多,结果就会显示在非连续的列中。

╔══════════╦══════════════════╦══════════════════╦══════════════════╦══════╦══════════════════╦══════╗
║ Project  ║       Col1       ║       Col2       ║       Col3       ║ Col4 ║       Col5       ║ Col6 ║
╠══════════╬══════════════════╬══════════════════╬══════════════════╬══════╬══════════════════╬══════╣
║ ProjectA ║ \\Server1\Share1 ║ \\Server2\Share1 ║ NULL             ║ NULL ║ NULL             ║ NULL ║
║ ProjectB ║ \\Server1\Share2 ║ \\Server2\Share3 ║ \\Server6\Share2 ║ NULL ║ NULL             ║ NULL ║
║ ProjectC ║ \\Server8\Share2 ║ NULL             ║ NULL             ║ NULL ║ NULL             ║ NULL ║
║ ProjectD ║ NULL             ║ \\Server5\Share9 ║ NULL             ║ NULL ║ \\ServerX\ShareY ║ NULL ║
╚══════════╩══════════════════╩══════════════════╩══════════════════╩══════╩══════════════════╩══════╝

使用GROUP BY 应该如此。

╔══════════╦══════════════════╦══════════════════╦══════════════════╦══════╦══════╦══════╗
║ Project  ║       Col1       ║       Col2       ║       Col3       ║ Col4 ║ Col5 ║ Col6 ║
╠══════════╬══════════════════╬══════════════════╬══════════════════╬══════╬══════╬══════╣
║ ProjectA ║ \\Server1\Share1 ║ \\Server2\Share1 ║ NULL             ║ NULL ║ NULL ║ NULL ║
║ ProjectB ║ \\Server1\Share2 ║ \\Server2\Share3 ║ \\Server6\Share2 ║ NULL ║ NULL ║ NULL ║
║ ProjectC ║ \\Server8\Share2 ║ NULL             ║ NULL             ║ NULL ║ NULL ║ NULL ║
║ ProjectD ║ \\Server5\Share9 ║ \\ServerX\ShareY ║ NULL             ║ NULL ║ NULL ║ NULL ║
╚══════════╩══════════════════╩══════════════════╩══════════════════╩══════╩══════╩══════╝

数据:

CREATE TABLE #mytable(
   Project  VARCHAR(80) NOT NULL 
  ,Location VARCHAR(160) NOT NULL
);
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server1\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server2\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server6\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server1\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server2\Share3');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectC','\\Server8\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\Server5\Share9');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\ServerX\ShareY');

<强>查询:

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

SELECT @max = MAX(c)
FROM (
  SELECT Project, COUNT(DISTINCT Location) AS c
  FROM #mytable
  GROUP BY Project) AS s;

SET @cols = STUFF(     
            (SELECT ', ' +  '[' + convert(varchar, c.n) + '] AS Column' + convert(varchar, 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 ',' +  '[' + convert(varchar, 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 Project, ' + @cols + ' from 
            (
                select Project, Location,
                [rn] = RANK() OVER (PARTITION BY Project ORDER BY Location)
                from #mytable
                GROUP BY Project, Location
            ) x
            pivot 
            (
                max(Location)
                for rn in (' + @cols_piv + ')
            ) p ';

EXEC [dbo].[sp_executesql]
    @query;