在SQL Server中转置表

时间:2011-12-08 10:23:26

标签: sql sql-server pivot transpose

我正在为我的应用程序使用SQL Server 2005。 我的存储过程中有一个表,它有两列,C1和C2。我想转置这个表,使列C1的值成为列。 转置之前(表1):

C1  C2
M1  U1
M1  U2
M1  U3
M2  U4
M2  U5

转置后(表2):

M1  M2
U1  U4
U2  U5
U3  NULL

在表1中,不同值(M1,M2)的数量可以变化。因此,Table2中的列不能修复。

请提供实现相同目标的解决方案。

3 个答案:

答案 0 :(得分:2)

对于此类数据转换,您将需要使用SQL Server 2005+中提供的PIVOT函数。有两种方法可以应用 pivot 功能。

如果您提前知道这些值,则可以对查询中的值进行硬编码。与此类似:

select M1, M2
from
(
  select c1, c2,
    row_number() over(partition by c1 order by c1, c2) rn
  from yourtable
) src
pivot
(
  max(c2)
  for c1 in (M1, M2)
) piv

请参阅SQL Fiddle with Demo

但是,如果要将未知数量的值转换为列,则可以使用动态SQL在运行时创建查询。

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(C1) 
                    from yourtable
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT ' + @cols + ' from 
             (
                select C1, C2,
                  row_number() over(partition by c1 order by c1, c2) rn
                from yourtable
            ) x
            pivot 
            (
                max(C2)
                for C1 in (' + @cols + ')
            ) p '

execute(@query)

请参阅SQL Fiddle with Demo

两者都会给出相同的结果,不同之处在于动态版本是灵活的,如果值会改变:

| M1 |     M2 |
---------------
| U1 |     U4 |
| U2 |     U5 |
| U3 | (null) |

答案 1 :(得分:0)

这可能是动态sql是你的朋友的情况。假设您的基表名称是“Transpose”:

--Get a Unique List of values from C1 --> These will be our column headers
DECLARE @Unique TABLE (C1 VARCHAR(25), fUsed BIT DEFAULT 0);
INSERT INTO @Unique (C1) SELECT DISTINCT C1 FROM Transpose;

DECLARE @TransSQL NVARCHAR(4000);
DECLARE @ColID NVARCHAR(25);

SET @TransSQL = N'SELECT ' 

--Loop over unique C1 values and construct the select statement
SELECT @ColID = (SELECT TOP 1 C1 FROM @Unique WHERE fUSed = 0);
WHILE @@ROWCOUNT <> 0 AND @ColID IS NOT NULL
BEGIN
    SET @TransSQL = @TransSQL + 'CASE C1 WHEN ' + '''' + @ColID + '''' + ' THEN C2 ELSE NULL END AS ' + @ColID + ', '

    --Update flag in table so we don't use this field again
    UPDATE u SET fUsed = 1 FROM @Unique u WHERE C1 = @ColID;
    SELECT @ColID = (SELECT TOP 1 C1 FROM @Unique WHERE fUSed = 0);
END

--Remove Trailing comma and add FROM clause
DECLARE @SQL NVARCHAR(4000)
SET @SQL = LEFT(@TransSQL,LEN(@TransSQL) -1) + ' FROM Transpose'

--For debugging purposes
PRINT @SQL;

--Execute the dynamic sql
EXEC sp_executesql @SQL;

这不会达到您所描述的问题的确切布局,因为它们不是将结果分组的关键字段。相反,输出将如下所示:

M1     M2      M3
U1     NULL    NULL
U2     NULL    NULL
U3     NULL    NULL
NULL   U4      NULL
NULL   U5      NULL
NULL   NULL    U6

如果您的基表有一个关键字段,则可以稍微修改查询以按键字段对结果进行分组,并且可能会更接近您对结果数据的既定目标。

答案 2 :(得分:0)

这不是你想要的确切结果,但你可以尝试这个

;WITH TAB1 AS
( SELECT  [ResponsibleID] C1,[ActionID] C2,1 ORD
  FROM [TEST].[dbo].[yourtable]
  WHERE 1=1
  )


 SELECT 
    CASE WHEN M1=1 THEN ActionID ELSE NULL END M1,
    CASE WHEN M2=1 THEN ActionID ELSE NULL END M2 
 FROM TAB1
 PIVOT(AVG(ORD) FOR RESPONSIBLEID IN ([M1],[M2])) AS ABC