SQL Server:将行转换为列

除了写光标读取每一行并将其填充到列,其他任何替代方法,如果我需要将每行转换为列?

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 

列数不固定。

输出:我刚分配n / a作为占位符,在该交集没有数据。

 TimeSec A1 A2 A3 A4 1378700244 3.75 n/an/an/a 1378700245 30 n/an/an/a 1378700304 1.2 n/an/an/a 1378700305 n/a 56 n/an/a 1378700344 n/a 11 n/an/a 1378700345 n/an/a 0.53 n/a 1378700364 n/an/an/a 4 1378700365 14.5 n/an/an/a 1378700384 144 n/an/a 10 

希望你能与我分享一些提示。 谢谢。

一种方法来做到这一点,如果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 

或者如果你确定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 

或与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 

输出(带NULL ):

时间秒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

如果您必须dynamic地将TagID值计算出来,则使用dynamicSQL

 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) 

SQL Server有一个PIVOT命令可能是你正在寻找的。

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

如果列不是常量,那么你必须把它和一些dynamic的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); 

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

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

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

垂直扩展

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

横向扩展

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

性能细分

就IO而言,脚本的XML版本的统计信息几乎与PIVOT相似 – 唯一的区别是XML对dtTranspose表进行了第二次扫描,但是这次是从逻辑读取数据高速caching中进行的。

您可以在本文中find更多关于这些解决scheme的信息(包括一些实际的T-SQL示例): https ://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/

基于bluefeet的解决scheme ,这里是一个使用dynamicsql生成转置表的存储过程。 它要求所有的字段都是数字型的,除了转置列(在结果表中将是标题的列):

 /****** Object: StoredProcedure [dbo].[SQLTranspose] Script Date: 11/10/2015 7:08:02 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: Paco Zarate -- Create date: 2015-11-10 -- Description: SQLTranspose dynamically changes a table to show rows as headers. It needs that all the values are numeric except for the field using for transposing. -- Parameters: @TableName - Table to transpose -- @FieldNameTranspose - Column that will be the new headers -- Usage: exec SQLTranspose <table>, <FieldToTranspose> -- table and FIeldToTranspose should be written using single quotes -- ============================================= ALTER PROCEDURE [dbo].[SQLTranspose] -- Add the parameters for the stored procedure here @TableName NVarchar(MAX) = '', @FieldNameTranspose NVarchar(MAX) = '' AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; DECLARE @colsUnpivot AS NVARCHAR(MAX), @query AS NVARCHAR(MAX), @queryPivot AS NVARCHAR(MAX), @colsPivot as NVARCHAR(MAX), @columnToPivot as NVARCHAR(MAX), @tableToPivot as NVARCHAR(MAX), @colsResult as xml select @tableToPivot = @TableName; select @columnToPivot = @FieldNameTranspose select @colsUnpivot = stuff((select ','+quotename(C.name) from sys.columns as C where C.object_id = object_id(@tableToPivot) and C.name <> @columnToPivot for xml path('')), 1, 1, '') set @queryPivot = 'SELECT @colsResult = (SELECT '','' + quotename('+@columnToPivot+') from '+@tableToPivot+' t where '+@columnToPivot+' <> '''' FOR XML PATH(''''), TYPE)' exec sp_executesql @queryPivot, N'@colsResult xml out', @colsResult out select @colsPivot = STUFF(@colsResult.value('.', 'NVARCHAR(MAX)'),1,1,'') set @query = 'select name, rowid, '+@colsPivot+' from ( select '+@columnToPivot+' , name, value, ROW_NUMBER() over (partition by '+@columnToPivot+' order by '+@columnToPivot+') as rowid from '+@tableToPivot+' unpivot ( value for name in ('+@colsUnpivot+') ) unpiv ) src pivot ( sum(value) for '+@columnToPivot+' in ('+@colsPivot+') ) piv order by rowid' exec(@query) END 

我有一个稍微不同的要求,我不得不有select性地将列转换成行。

该表有一些列:

 create table tbl (ID, PreviousX, PreviousY, CurrentX, CurrentY) 

我需要“ Previous和“ Current列,以及XY行。 在静态桌面上生成的笛卡尔积很好地工作,例如:

 select ID, max(case when metric='X' then PreviousX case when metric='Y' then PreviousY end) as Previous, max(case when metric='X' then CurrentX case when metric='Y' then CurrentY end) as Current from tbl inner join /* Cartesian product - transpose by repeating row and picking appropriate metric column for period */ ( VALUES (1, 'X'), (2, 'Y')) AS x (sort, metric) ON 1=1 group by ID order by ID, sort