简单的方法来转置Sql中的列和行?
我如何简单地在SQL中切换行? 有没有简单的命令转置?
即打开这个结果:
Paul | John | Tim | Eric Red 1 5 1 3 Green 8 4 3 5 Blue 2 2 9 1
进入这个:
Red | Green | Blue Paul 1 8 2 John 5 4 2 Tim 1 3 9 Eric 3 5 1
对于这种情况, PIVOT
似乎太复杂了。
有几种方法可以转换这些数据。 在你原来的文章中,你指出PIVOT
对于这个场景似乎太复杂了,但是它可以非常容易地使用SQL Server中的UNPIVOT
和PIVOT
函数来应用。
但是,如果您无法访问这些函数,则可以使用UNION ALL
复制UNPIVOT
,然后将具有CASE
语句的聚合函数复制到PIVOT
:
创build表格:
CREATE TABLE yourTable([color] varchar(5), [Paul] int, [John] int, [Tim] int, [Eric] int); INSERT INTO yourTable ([color], [Paul], [John], [Tim], [Eric]) VALUES ('Red', 1, 5, 1, 3), ('Green', 8, 4, 3, 5), ('Blue', 2, 2, 9, 1);
Union All,Aggregate和CASE版本:
select name, sum(case when color = 'Red' then value else 0 end) Red, sum(case when color = 'Green' then value else 0 end) Green, sum(case when color = 'Blue' then value else 0 end) Blue from ( select color, Paul value, 'Paul' name from yourTable union all select color, John value, 'John' name from yourTable union all select color, Tim value, 'Tim' name from yourTable union all select color, Eric value, 'Eric' name from yourTable ) src group by name
看演示与SQL小提琴
UNION ALL
通过将Paul, John, Tim, Eric
列转换为单独的行来执行数据的UNPIVOT
。 然后你用case
语句应用聚合函数sum()
来获取每个color
的新列。
Unpivot和Pivot静态版本:
SQL Server中的UNPIVOT
和PIVOT
函数使得这个转换变得更容易。 如果您知道所有要转换的值,则可以将它们硬编码为静态版本以获取结果:
select name, [Red], [Green], [Blue] from ( select color, name, value from yourtable unpivot ( value for name in (Paul, John, Tim, Eric) ) unpiv ) src pivot ( sum(value) for color in ([Red], [Green], [Blue]) ) piv
看演示与SQL小提琴
使用UNPIVOT
的内部查询执行与UNION ALL
相同的function。 它获取列的列表并将其转换为行,然后PIVOT
执行最终转换为列。
dynamic枢轴版本:
如果你有一个未知数的列(例如Paul, John, Tim, Eric
),然后使用未知数量的颜色进行转换,则可以使用dynamicsql将列表生成为UNPIVOT
,然后是PIVOT
:
DECLARE @colsUnpivot AS NVARCHAR(MAX), @query AS NVARCHAR(MAX), @colsPivot as NVARCHAR(MAX) select @colsUnpivot = stuff((select ','+quotename(C.name) from sys.columns as C where C.object_id = object_id('yourtable') and C.name <> 'color' for xml path('')), 1, 1, '') select @colsPivot = STUFF((SELECT ',' + quotename(color) from yourtable t FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'') set @query = 'select name, '+@colsPivot+' from ( select color, name, value from yourtable unpivot ( value for name in ('+@colsUnpivot+') ) unpiv ) src pivot ( sum(value) for color in ('+@colsPivot+') ) piv' exec(@query)
看演示与SQL小提琴
dynamic版本将同时查询yourtable
和sys.columns
表,以生成UNPIVOT
和PIVOT
的项目列表。 然后将其添加到要执行的查询string中。 dynamic版本的优点是,如果你有一个不断变化的colors
和/或names
列表,这将在运行时生成列表。
所有三个查询将产生相同的结果:
| NAME | RED | GREEN | BLUE | ----------------------------- | Eric | 3 | 5 | 1 | | John | 5 | 4 | 2 | | Paul | 1 | 8 | 2 | | Tim | 1 | 3 | 9 |
这通常需要您事先知道所有列和行标签。 正如您在下面的查询中看到的那样,这些标签都完全在UNPIVOT和(重新)PIVOT操作中列出。
MS SQL Server 2012架构设置 :
create table tbl ( color varchar(10), Paul int, John int, Tim int, Eric int); insert tbl select 'Red' ,1 ,5 ,1 ,3 union all select 'Green' ,8 ,4 ,3 ,5 union all select 'Blue' ,2 ,2 ,9 ,1;
查询1 :
select * from tbl unpivot (value for name in ([Paul],[John],[Tim],[Eric])) up pivot (max(value) for color in ([Red],[Green],[Blue])) p
结果 :
| NAME | RED | GREEN | BLUE | ----------------------------- | Eric | 3 | 5 | 1 | | John | 5 | 4 | 2 | | Paul | 1 | 8 | 2 | | Tim | 1 | 3 | 9 |
补充笔记:
- 给定一个表名,你可以使用local-name()来确定sys.columns中的所有列名或者FOR XML欺骗。
- 您还可以使用FOR XML构build不同颜色的列表(或一列的值)。
- 以上可以组合成一个dynamic的sql批处理任何表。
我想指出几个更多的解决scheme来转换SQL中的列和行。
第一个是 – 使用CURSOR。 尽pipe专业人士普遍认为远离SQL Server游标,但仍然有推荐使用游标的例子。 无论如何,游标给我们提供了另一种将行转换为列的选项。
-
垂直扩展
与PIVOT类似,光标具有dynamic能力,可以在数据集扩展时添加更多行,以包含更多的策略编号。
-
横向扩展
与PIVOT不同的是,由于光标可以扩展为包含新添加的文档,因此在该区域中的performance更胜一筹,而不会更改脚本。
-
性能细分
使用CURSOR将行转换为列的主要限制是与一般使用游标相关的缺点 – 它们的性能成本非常高。 这是因为Cursor为每个FETCH NEXT操作生成一个单独的查询。
将行转换为列的另一个解决scheme是使用XML。
将行转换为列的XML解决scheme基本上是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> -- ============================================= 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
您可以使用此命令提供的表对其进行testing:
exec SQLTranspose 'yourTable', 'color'
我UnPivot
并将结果存储在CTE
并使用CTE
在Pivot
操作中。
演示
with cte as ( select 'Paul' as Name, color, Paul as Value from yourTable union all select 'John' as Name, color, John as Value from yourTable union all select 'Tim' as Name, color, Tim as Value from yourTable union all select 'Eric' as Name, color, Eric as Value from yourTable ) select Name, [Red], [Green], [Blue] from ( select * from cte ) as src pivot ( max(Value) for color IN ([Red], [Green], [Blue]) ) as Dtpivot;
我喜欢分享我使用的代码来转置基于+ bluefeet答案的拆分文本。 在这个问题上,我作为MS SQL 2005中的一个过程实现
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: ELD. -- Create date: May, 5 2016. -- Description: Transpose from rows to columns the user split function. -- ============================================= CREATE PROCEDURE TransposeSplit @InputToSplit VARCHAR(8000) ,@Delimeter VARCHAR(8000) = ',' AS BEGIN 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 = '#tempSplitedTable' SELECT @columnToPivot = 'col_number' CREATE TABLE #tempSplitedTable ( col_number INT ,col_value VARCHAR(8000) ) INSERT INTO #tempSplitedTable ( col_number ,col_value ) SELECT ROW_NUMBER() OVER ( ORDER BY ( SELECT 100 ) ) AS RowNumber ,item FROM [DB].[ESCHEME].[fnSplit](@InputToSplit, @Delimeter) SELECT @colsUnpivot = STUFF(( SELECT ',' + quotename(C.NAME) FROM [tempdb].sys.columns AS C WHERE C.object_id = object_id('tempdb..' + @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 ( MAX(value) for ' + @columnToPivot + ' in (' + @colsPivot + ') ) piv order by rowid' EXEC (@query) DROP TABLE #tempSplitedTable END GO
我将这个解决scheme与关于如何sorting行的信息( SQLAuthority.com )和MSDN上的split函数( social.msdn.microsoft.com )
当你执行prodecure时
DECLARE @RC int DECLARE @InputToSplit varchar(MAX) DECLARE @Delimeter varchar(1) set @InputToSplit = 'hello|beautiful|world' set @Delimeter = '|' EXECUTE @RC = [TransposeSplit] @InputToSplit ,@Delimeter GO
你获得了下一个结果
name rowid 1 2 3 col_value 1 hello beautiful world