【问题标题】:SQL: Convert Data For One Column To Multiple ColumnsSQL:将一列的数据转换为多列
【发布时间】:2015-10-12 12:42:55
【问题描述】:

我尝试使用诸如 Pivot 之类的东西寻找解决问题的方法,但它们似乎没有给出我正在寻找的输出,因为它们似乎将行中的特定值映射到列。

我有两列,第一列包含“关键”字段,第二列正在更改数据。例如

╔══════════╦══════════════════╗
║ 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。

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

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

提前致谢。

忍者

【问题讨论】:

  • 为什么PROJECT D 的结果中有 2 条记录?而且没有一条记录ProjectD \\Server5\Share9 \\ServerX\ShareY
  • 那是我的错误,它从盒子底部掉了,所以我没有注意到它。已更新帖子。

标签: sql sql-server tsql


【解决方案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 并生成透视列列表:

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;

【讨论】:

  • 感谢 lad2025 的快速响应。非常有帮助。为了扩展它,您提到使用 Dynamic-SQL 生成一个透视列列表。你有一个大致的例子吗?再次感谢。
  • @Ninja 查看更新的答案,完全动态的解决方案;)
  • 我注意到的一件事是,如果我不对排名表进行分组,因为它只是重复数据,我会得到奇怪的结果。下面的小改动解决了这个问题。 WITH CTE AS (SELECT Project , WS , RANK() OVER (PARTITION BY Project ORDER BY Location) [RN] FROM #mytable GROUP BY PROJECT, Location
  • @Ninja 对不起,我不明白。请分叉我的查询并提供示例链接。在您的数据中没有列 WS。我希望它适用于您的数据。
  • 感谢您的所有帮助@lad2025。我已经设法让它现在运行。非常感谢。
【解决方案2】:

归功于@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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 2017-12-01
    • 2019-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多