在Sql中转换列和行的简单方法?

ede*_*zie 100 sql t-sql sql-server pivot

如何在SQL中简单地使用行切换列?是否有任何简单的转置命令?

即转此结果:

        Paul  | John  | Tim  |  Eric
Red     1       5       1       3
Green   8       4       3       5
Blue    2       2       9       1
Run Code Online (Sandbox Code Playgroud)

进入这个:

        Red  | Green | Blue
Paul    1       8       2
John    5       4       2
Tim     1       3       9
Eric    3       5       1
Run Code Online (Sandbox Code Playgroud)

PIVOT 这种情况看起来太复杂了.

Tar*_*ryn 131

您可以通过多种方式转换此数据.在您的原始帖子中,您声明PIVOT对于此方案似乎过于复杂,但可以使用SQL Server中的UNPIVOTPIVOT函数非常轻松地应用它.

但是,如果您无权访问这些函数,则可以使用UNION ALLto 复制UNPIVOT函数,然后使用以下CASE语句将聚合函数复制到PIVOT:

创建表:

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);
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo

UNION ALL执行与UNPIVOT通过变换列中的数据Paul, John, Tim, Eric进行分离.然后将聚合函数sum()case语句一起应用以获取每个语句的新列color.

Unpivot和Pivot静态版本:

SQL Server中的UNPIVOTPIVOT函数都使这种转换更容易.如果您知道要转换的所有值,则可以将它们硬编码为静态版本以获得结果:

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
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo

内部查询与UNPIVOT执行相同的功能UNION ALL.它获取列列表并将其转换为行,PIVOT然后执行最终转换为列.

动态枢轴版本:

如果你有一个未知数量的列(Paul, John, Tim, Eric在你的例子中)然后有一个未知数量的颜色要转换你可以使用动态sql生成列表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)
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo

动态版本查询两个yourtable然后查询sys.columns表以生成项目列表UNPIVOTPIVOT.然后将其添加到要执行的查询字符串中.动态版本的优点是,如果您有更改列表colors和/或names这将在运行时生成列表.

所有三个查询都会产生相同的结果:

| NAME | RED | GREEN | BLUE |
-----------------------------
| Eric |   3 |     5 |    1 |
| John |   5 |     4 |    2 |
| Paul |   1 |     8 |    2 |
|  Tim |   1 |     3 |    9 |
Run Code Online (Sandbox Code Playgroud)

  • 您有什么理由没有将这个出色的答案标记为答案吗?给@bluefeet 一枚奖章! (2认同)

Ric*_*iwi 18

这通常要求您事先知道所有列和行标签.正如您在下面的查询中看到的那样,标签全部列在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;
Run Code Online (Sandbox Code Playgroud)

查询1:

select *
from tbl
unpivot (value for name in ([Paul],[John],[Tim],[Eric])) up
pivot (max(value) for color in ([Red],[Green],[Blue])) p
Run Code Online (Sandbox Code Playgroud)

结果:

| NAME | RED | GREEN | BLUE |
-----------------------------
| Eric |   3 |     5 |    1 |
| John |   5 |     4 |    2 |
| Paul |   1 |     8 |    2 |
|  Tim |   1 |     3 |    9 |
Run Code Online (Sandbox Code Playgroud)

补充说明:

  1. 给定表名,您可以使用local-name()确定sys.columns或FOR XML trickery中的所有列名.
  2. 您还可以使用FOR XML构建不同颜色(或一列的值)的列表.
  3. 以上可以组合成动态sql批处理来处理任何表.


小智 10

我想指出几个在SQL中转置列和行的解决方案.

第一个是 - 使用CURSOR.虽然专业社区的普遍共识是远离SQL Server游标,但仍然存在建议使用游标的情况.无论如何,游标向我们提供了另一种将行转换为列的选项.

  • 垂直扩展

    与PIVOT类似,当您的数据集扩展以包含更多策略编号时,游标具有附加更多行的动态功能.

  • 水平扩展

    与PIVOT不同,光标在此区域表现优异,因为它可以扩展以包含新添加的文档,而无需更改脚本.

  • 性能细分

    使用CURSOR将行转换为列的主要限制是与使用游标一般相关的缺点 - 它们会带来显着的性能成本.这是因为Cursor为每个FETCH NEXT操作生成单独的查询.

将行转换为列的另一种解决方案是使用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/


Pac*_*ate 7

基于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>
-- =============================================
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
Run Code Online (Sandbox Code Playgroud)

您可以使用此命令提供的表来测试它:

exec SQLTranspose 'yourTable', 'color'
Run Code Online (Sandbox Code Playgroud)

  • 对于所有字段 nvarchar(max) 只需在从末尾开始的第 6 行中将 sum(value) 更改为 max(value) (3认同)