SQL Server:将行转置为列

use*_*739 29 sql t-sql sql-server-2005

除了将光标写入每行并将其填充到列中之外,如果我需要将每行转置为列,还有其他任何替代方法吗?

TimeSeconds TagID Value
1378700244  A1    3.75
1378700245  A1    30
1378700304  A1    1.2
1378700305  A2    56
1378700344  A2    11
1378700345  A3    0.53
1378700364  A1    4
1378700365  A1    14.5
1378700384  A1    144
1378700384  A4    10
Run Code Online (Sandbox Code Playgroud)

列数不固定.

输出:我刚刚将n/a指定为占位符,该交集中没有数据.

TimeSec     A1    A2    A3    A4
1378700244  3.75  n/a   n/a   n/a
1378700245  30    n/a   n/a   n/a
1378700304  1.2   n/a   n/a   n/a
1378700305  n/a   56    n/a   n/a
1378700344  n/a   11    n/a   n/a
1378700345  n/a   n/a   0.53  n/a
1378700364  n/a   n/a   n/a   4
1378700365  14.5  n/a   n/a   n/a
1378700384  144   n/a   n/a   10
Run Code Online (Sandbox Code Playgroud)

希望您能与我分享一些提示.谢谢.

pet*_*erm 40

如果tagID预先知道值,则执行此操作的一种方法是使用条件聚合

SELECT TimeSeconds,
       COALESCE(MAX(CASE WHEN TagID = 'A1' THEN Value END), 'n/a') A1,
       COALESCE(MAX(CASE WHEN TagID = 'A2' THEN Value END), 'n/a') A2,
       COALESCE(MAX(CASE WHEN TagID = 'A3' THEN Value END), 'n/a') A3,
       COALESCE(MAX(CASE WHEN TagID = 'A4' THEN Value END), 'n/a') A4
  FROM table1
 GROUP BY TimeSeconds
Run Code Online (Sandbox Code Playgroud)

或者如果你没有使用NULL值而不是'n/a'

SELECT TimeSeconds,
       MAX(CASE WHEN TagID = 'A1' THEN Value END) A1,
       MAX(CASE WHEN TagID = 'A2' THEN Value END) A2,
       MAX(CASE WHEN TagID = 'A3' THEN Value END) A3,
       MAX(CASE WHEN TagID = 'A4' THEN Value END) A4
  FROM table1
 GROUP BY TimeSeconds
Run Code Online (Sandbox Code Playgroud)

或者 PIVOT

SELECT TimeSeconds, A1, A2, A3, A4
  FROM
(
  SELECT TimeSeconds, TagID, Value
    FROM table1
) s
PIVOT
(
  MAX(Value) FOR TagID IN (A1, A2, A3, A4)
) p
Run Code Online (Sandbox Code Playgroud)

输出(带NULLs):

TimeSeconds A1      A2     A3    A4
----------- ------- ------ ----- -----
1378700244  3.75    NULL   NULL  NULL
1378700245  30.00   NULL   NULL  NULL
1378700304  1.20    NULL   NULL  NULL
1378700305  NULL    56.00  NULL  NULL
1378700344  NULL    11.00  NULL  NULL
1378700345  NULL    NULL   0.53  NULL
1378700364  4.00    NULL   NULL  NULL
1378700365  14.50   NULL   NULL  NULL
1378700384  144.00  NULL   NULL  10.00

如果必须TagID动态计算值,请使用动态SQL

DECLARE @cols NVARCHAR(MAX), @sql NVARCHAR(MAX)

SET @cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(TagID)
            FROM Table1
            ORDER BY 1
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'),1,1,'')

SET @sql = 'SELECT TimeSeconds, ' + @cols + '
              FROM
            (
              SELECT TimeSeconds, TagID, Value
                FROM table1
            ) s
            PIVOT
            (
              MAX(Value) FOR TagID IN (' + @cols + ')
            ) p'

EXECUTE(@sql)
Run Code Online (Sandbox Code Playgroud)


sar*_*rme 10

SQL Server有一个可能正在寻找的PIVOT命令.

select * from Tag
pivot (MAX(Value) for TagID in ([A1],[A2],[A3],[A4])) as TagTime;
Run Code Online (Sandbox Code Playgroud)

如果列不是常量,则必须将其与某些动态SQL结合使用.

DECLARE @columns AS VARCHAR(MAX);
DECLARE @sql AS VARCHAR(MAX);

select @columns = substring((Select DISTINCT ',' + QUOTENAME(TagID) FROM Tag FOR XML PATH ('')),2, 1000);

SELECT @sql =

'SELECT *
FROM TAG
PIVOT 
(
  MAX(Value) 
  FOR TagID IN( ' + @columns + ' )) as TagTime;';

 execute(@sql);
Run Code Online (Sandbox Code Playgroud)


小智 10

在这种情况下可能适合的另一个选择是使用XML

将行转换为列的XML选项基本上是PIVOT的最佳版本,因为它解决了动态列限制. 

该脚本的XML版本通过结合使用XML Path,动态T-SQL和一些内置函数(即STUFF,QUOTENAME)来解决此限制.

垂直扩展

与PIVOT和Cursor类似,可以在脚本的XML版本中检索新添加的策略,而无需更改原始脚本.

水平扩展

与PIVOT不同,可以在不更改脚本的情况下显示新添加的文档.

性能细分

就IO而言,脚本的XML版本的统计信息几乎与PIVOT类似 - 唯一的区别是XML有第二次扫描dtTranspose表,但这次是从逻辑读取 - 数据缓存.

您可以在本文中找到有关这些解决方案的更多信息(包括一些实际的T-SQL exmaples):https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/