根据组将行转换为列

时间:2015-08-07 12:12:47

标签: sql-server

你有一个场景,我已经做了这样的SQL查询输出:

number  x1  y1  z1  x2  y2  z2  x3  y3  z3
 a       1  10  aa                        
 a       2   8  aa
 a       3   6  aa
 b       4   11 bb
 b       5   6  bb

现在我被困在这里:如何将上述行转换为:

number  x1  y1  z1  x2  y2  z2  x3  y3  z3
 a       1  10  aa  2   8   aa   3   6  aa                    
 b       4  11  bb  5   6   bb

每个数字最多可包含10个x_,y_,z_values(x1,y1,z1,...,x10,y10,z10)

那怎么能实现呢?

1 个答案:

答案 0 :(得分:0)

我不知道这背后的逻辑是什么,但是可以做到。

首先,我使用它来初始化您作为测试集提供的相同数据:

CREATE TABLE TESTTABLE (number varchar(5), x1 varchar(5), y1 varchar(5), z1 varchar(5)
     , x2 varchar(5), y2 varchar(5), z2 varchar(5), x3 varchar(5), y3 varchar(5), z3 varchar(5))

INSERT INTO TESTTABLE (number,x1,y1,z1)
VALUES ('a','1','10','aa')
    ,('a','2','8','aa')
    ,('a','3','6','aa')
    ,('b','4','11','bb')
    ,('b','5','6','bb')

现在查询..如果您通过硬编码最大列数来冷却,您可以选择以下内容:

;WITH CTE AS 
    (SELECT ROW_NUMBER() OVER (PARTITION BY number ORDER BY x1 asc) RN, *
    FROM TESTTABLE)
SELECT c.number, C.x1, C.y1, C.z1
    , C2.x1 as x2, C2.y1 as y2, C2.z1 as z2
    , C3.x1 as x3, C3.y1 as y3, C3.z1 as z3
FROM CTE C
LEFT JOIN CTE C2 ON C2.number = C.number AND C2.RN = 2
LEFT JOIN CTE C3 ON C3.number = C.number AND C3.RN = 3
WHERE C.RN=1

但是,如果你想动态只显示有数据的列,那么就可以完成这项任务(这很难看,但我唯一担心的是它可以工作:)。

DECLARE @TSQLHeader nvarchar(max) = '', @TSQLTrailer nvarchar(max) = '
FROM CTE C'

DECLARE @MaxCols INT = 
    (SELECT TOP 1 COUNT(*) CNT FROM TESTTABLE GROUP BY number ORDER BY CNT DESC)
    , @CurCol INT = 2

SELECT @TSQLHeader = 
';WITH CTE AS 
    (SELECT ROW_NUMBER() OVER (PARTITION BY number ORDER BY x1 asc) RN, *
    FROM TESTTABLE)
SELECT c.number, C.x1, C.y1, C.z1'

WHILE @CurCol <= @MaxCols
BEGIN
    SELECT @TSQLHeader = @TSQLHeader+'
    , C'+CAST(@CurCol AS VARCHAR(5))+'.x1 as x'+CAST(@CurCol AS VARCHAR(5))+', C'+CAST(@CurCol AS VARCHAR(5))+'.y1 as y'+CAST(@CurCol AS VARCHAR(5))
    +', C'+CAST(@CurCol AS VARCHAR(5))+'.z1 as z'+CAST(@CurCol AS VARCHAR(5))
    SELECT @TSQLTrailer = @TSQLTrailer+'
LEFT JOIN CTE C'+CAST(@CurCol AS VARCHAR(5))+' ON C'+CAST(@CurCol AS VARCHAR(5))+'.number = C.number AND C'+CAST(@CurCol AS VARCHAR(5))+'.RN = '+CAST(@CurCol AS VARCHAR(5))

    SET @CurCol = @CurCol+1
END 

-- Combine the header and trailer, producing a finished script
SELECT @TSQLHeader = @TSQLHeader + @TSQLTrailer+'
WHERE C.RN=1'

EXEC sp_executesql @TSQLHeader

但是,当然,如果您在问题中遗漏了任何内容,则必须对这些内容进行彻底更改以适应不同的内容。