如何在SQL中简单地切换列和行?

ie将此结果转化为:

        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 and PIVOT个函数可以非常容易地应用它.

但是,如果您无法访问这些函数,则可以使用UNION ALLUNPIVOT,然后使用CASE语句到PIVOT的聚合函数来复制这些函数:

Create Table:

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 and CASE Version:

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 Fiddle with Demo

UNION ALL通过将列Paul, John, Tim, Eric转换为单独的行来执行数据的UNPIVOT.然后应用聚合函数sum()case语句,以获得每个color的新列.

Unpivot and Pivot Static Version:

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

SQL Fiddle with Demo

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

Dynamic Pivot Version:

如果要转换的列数未知(在示例中为Paul, John, Tim, Eric),然后 colored颜色 数未知,则可以使用动态sql生成UNPIVOTPIVOT的列表:

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

Sql相关问答推荐

AWS Athena将SON对象数组解析到行

IS True操作员

判断Pyspark生成的SQL查询

为什么TRY_CONVERT返回一个XML验证错误而不是NULL?

PostgreSQL:查找继承表中的特定记录属于哪个表

根据日期 Select ID 的上一条记录

按二维数组的第一个元素排序

Grafana SQL 模板变量(值、文本)

识别SQL Server中的重复数字

SQL的左连接在多对多关系情况下使用

Oracle PL/SQL长期运行问题

当该日期的至少两条记录具有相同的持续时间或至少一条记录的持续时间为 0 时,如何标记该日期的所有记录

在 MS Access VBA 中,如何测量显示查询的时间?

oracle中多行的跨日期范围的交集

在 SQL 查询中创建滚动日期

我需要遍历权重值表并确定每个权重是否有效

T-SQL 查询计算日期在其他列中定义的日期之间绑定的行数

SQLite 中的过滤运行总和视图

面对来自以下两个代码的不同输出

如何使用 pg-promise 创建要在复杂的 sql 查询中使用的 VALUES 列表?