SQL Server:将行转置为列
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20111418/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
SQL Server : Transpose rows to columns
提问by user3015739
Apart from writing the cursor reading each rows and populating it into columns, any other alternative if I need to transpose each rows into columns ?
除了编写游标读取每一行并将其填充到列中之外,如果我需要将每一行转换为列,还有其他选择吗?
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
The number of columns are not fixed.
列数不固定。
Output : I just assigned n/a as a placeholder for no data in that intersection.
输出:我只是将 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
Hope you can share with me some tips. Thanks.
希望你能和我分享一些技巧。谢谢。
回答by peterm
One way to do it if tagID
values are known upfront is to use conditional aggregation
如果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
or if you're OK with NULL
values instead of 'n/a'
或者如果你对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
or with PIVOT
或与 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
Output (with NULL
s):
输出(带NULL
s):
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
If you have to figure TagID
values out dynamically then use dynamic SQL
如果您必须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)
回答by sarme
SQL Server has a PIVOTcommand that might be what you are looking for.
SQL Server 有一个PIVOT命令,可能正是您要找的。
select * from Tag
pivot (MAX(Value) for TagID in ([A1],[A2],[A3],[A4])) as TagTime;
If the columns are not constant, you'll have to combine this with some dynamic SQL.
如果列不是常量,则必须将其与一些动态 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);
回答by FloresP
Another option that may be suitable in this situation is using XML
在这种情况下可能适用的另一种选择是使用 XML
The XML option to transposing rows into columns is basically an optimal version of the PIVOT in that it addresses the dynamic column limitation.?
将行转换为列的 XML 选项基本上是 PIVOT 的最佳版本,因为它解决了动态列限制。
The XML version of the script addresses this limitation by using a combination of XML Path, dynamic T-SQL and some built-in functions (i.e. STUFF, QUOTENAME).
该脚本的 XML 版本通过使用 XML 路径、动态 T-SQL 和一些内置函数(即 STUFF、QUOTENAME)的组合解决了这一限制。
Vertical expansion
纵向扩展
Similar to the PIVOT and the Cursor, newly added policies are able to be retrieved in the XML version of the script without altering the original script.
与 PIVOT 和 Cursor 类似,新添加的策略可以在脚本的 XML 版本中检索,而无需更改原始脚本。
Horizontal expansion
横向扩展
Unlike the PIVOT, newly added documents can be displayed without altering the script.
与 PIVOT 不同的是,无需更改脚本即可显示新添加的文档。
Performance breakdown
性能细分
In terms of IO, the statistics of the XML version of the script is almost similar to the PIVOT – the only difference is that the XML has a second scan of dtTranspose table but this time from a logical read – data cache.
在 IO 方面,XML 版本脚本的统计数据几乎与 PIVOT 相似——唯一的区别是 XML 对 dtTranspose 表进行了第二次扫描,但这次是从逻辑读取 - 数据缓存。
You can find some more about these solutions (including some actual T-SQL exmaples) in this article: https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/
您可以在本文中找到有关这些解决方案的更多信息(包括一些实际的 T-SQL 示例):https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/
回答by Paco Zarate
Based on the solutionfrom bluefeethere is a stored procedure that uses dynamic sql to generate the transposed table. It requires that all the fields are numeric except for the transposed column (the column that will be the header in the resulting table):
基于该解决方案从bluefeet这里是使用动态SQL生成转表的存储过程。它要求除了转置列(将成为结果表中的标题的列)之外的所有字段都是数字:
/****** 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
回答by Chris
I had a slightly different requirement, whereby I had to selectively transpose columns into rows.
我有一个稍微不同的要求,我必须有选择地将列转换为行。
The table had columns:
该表有列:
create table tbl (ID, PreviousX, PreviousY, CurrentX, CurrentY)
I needed columns for Previous
and Current
, and rows for X
and Y
. A Cartesian product generated on a static table worked nicely, eg:
我需要的列Previous
和Current
行,并X
和Y
。在静态表上生成的笛卡尔积效果很好,例如:
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