SQL 和 SPL 的基本静态转置对比

【摘要】
    转置功能常用报表等前端展现,将查询出来的数据转置成指定的显示格式。比如行转列,列转行,以及比较复杂的动态转置等等。SQL 和 SPL 是大家比较熟悉的程序语言,本文将探讨对于转置问题,这两种语言的解决方案和基本原理。如何简便快捷的处理转置运算,这里为你全程解析,并提供 SQL 和 SPL 示例代码。SQL 和 SPL 的基本静态转置对比

复制摘要

 

静态转置是指,转置后的表结构事先可以确定,目标结构不会根据数据进行动态的变化。

 

1. 行转列

行转列顾名思义,这个功能是实现行与列的转换,也就是将行中的值作为列名。PIVOT 的实际应用,基本都要跟随在分组聚合运算之后,通过分组把用于转置的列中每一行数据都处理成不重复的值后,再将各行的值作为列名来展开。PIVOT 的作用,其实就是将某一列的聚合结果,细分为多个更具体的列的聚合结果,以达到更直观的视觉效果。

 

【例 1】 根据学生成绩表,统计每个班的各科最高分,按列显示。部分数据如下:

CLASS STUDENTID SUBJECT SCORE
1 1 English 84
1 1 Math 77
1 1 PE 69
1 2 English 81
1 2 Math 80

期望得到如下格式结果:

CLASS MAX_MATH MAX_ENGLISH MAX_PE
1 97 96 97
2 97 96 97

 

SQL的解决方案:

在 SQL 中,PIVOT 函数可以用于实现行转列和列转行,但只有部分数据库的较新版本才支持。例如 ORACLE 从 11g 版本才开始支持 PIVOT,而 MYSQL 等数据库一直不支持,需要使用子查询分组汇总,再 LEFT JOIN 等方式实现行转列。在本文中我们以 ORACLE 11g 为例,SQL 语句如下:

 

   SELECT *

   FROM (

      SELECT

         CLASS, SUBJECT, SCORE

      FROM SCORES

   )

   PIVOT (

      MAX(SCORE) FOR SUBJECT

      IN (

         'Math' AS MAX_MATH,

         'English' AS MAX_ENGLISH,

         'PE' AS MAX_PE

      )

   )

 

SPL的解决方案:

    在 SPL 中提供了函数 A.pivot() 用于行列转置,默认为行转列。


A
1 =T("Scores.csv")
2 =A1.groups(CLASS,SUBJECT; max(SCORE):MAX_SCORE)
3 =A2.pivot(CLASS; SUBJECT, MAX_SCORE; "Math":"MAX_MATH", "English":"MAX_ENGLISH", "PE":"MAX_PE")

A1:从文件中导入学生成绩表。

A2:分组汇总每班各科的最高分。

A3:使用函数 A.pivot(),将每班各科最高分行转列。

 

SPL同样也支持从数据库中读取数据表,比如A1可以改为:


A
1 =connect("db").query("SELECT * FROM SCORES")

 

 

2. 列转行

列转行与行转列相反,每一个要转置的字段都生成新的一行,字段名或对应名称转化为新字段的值,原字段值则转为另一新字段的值。

 

【例 2】 根据奥运会奖牌总榜,生成每种奖牌的榜单。部分数据如下:

Game Nation Gold Silver Copper
30 USA 46 29 29
30 China 38 27 23
30 UK 29 17 19
30 Russia 24 26 32
30 Korea 13 8 7

期望得到如下格式结果:

GAME NATION MEDAL_TYPE MEDALS
30 USA GOLD 46
30 USA SILVER 29
30 USA COPPER 29
30 China GOLD 38
30 China SILVER 27

 

 

SQL的解决方案:

    在 SQL 中,可以使用 UNPIVOT 函数进行列转行:

 

   SELECT *

   FROM OLYMPIC

   UNPIVOT (

      MEDALS FOR MEDAL_TYPE IN (

          GOLD,SILVER,COPPER

      )

   )

 

SPL的解决方案:

    函数 A.pivot() 选项 @r 时用于列转行。


A
1 =T("Olympic.txt")
2 =A1.pivot@r(GAME,NATION; MEDAL_TYPE, MEDALS; GOLD, SILVER, COPPER)

A1:导入奥运会奖牌表。

A2:使用函数 A.pivot@r(),将字段 GOLD、SILVER、COPPER 进行列转行。

 

3. 行列互转

 

【例 3】 按渠道分类的销售表,按日期记录。部分数据如下:

YEAR MONTH ONLINE STORE
2020 1 2440 3746.2
2020 2 1863.4 448.0
2020 3 1813.0 624.8
2020 4 670.8 2464.8
2020 5 3730.0 724.5

期望得到如下格式结果:

CATEGORY 1 2 3
ONLINE 2440 1863.4 1813.0
STORE 3746.2 448.0 624.8

 

 

SQL的解决方案:

    这个问题单独用行转列或者列转行都无法实现,需要进行两次转换。首先列转行,将渠道类型转换为类别列的字段值:

YEAR MONTH CATEGORY AMOUNT
2020 1 ONLINE 2440
2020 1 STORE 3746.2
2020 2 ONLINE 1863.4
2020 2 STORE 448.0

 

然后行转列,将月份字段的值转换为列。SQL 语句如下:

 

   SELECT *

   FROM (

      SELECT *

      FROM MONTH_SALES

      UNPIVOT (

         AMOUNT FOR CATEGORY IN (

            "ONLINE",STORE

         )

      )

      WHERE YEAR=2020

   )

   PIVOT (

      MAX(AMOUNT) FOR MONTH

      IN (

         1 AS "1",2 AS "2",2 AS "3",

         4 AS "4",5 AS "5",6 AS "6",

         7 AS "7",8 AS "8",9 AS "9",

         10 AS "10",11 AS "11",12 AS "12"

      )

   )

 

在 SQL 中不允许将非常量表达式用于 PIVOT/UNPIVOT 值,所以行转列时需要枚举所有的月份。

 

SPL的解决方案:

    按照逻辑顺序使用函数 A.pivot@r()列转行,A.pivot() 行转列即可:


A
1 =T("MonthSales.csv").select(YEAR:2020)
2 =A1.pivot@r(YEAR,MONTH; CATEGORY, AMOUNT)
3 =A2.pivot(CATEGORY; MONTH, AMOUNT)

A1:导入销售表,并选出 2014 年的记录。

A2:使用函数 A.pivot@r() 列转行,将渠道类型转换为 CATEGORY 的字段值。

A3:使用函数 A.pivot() 行转列,将月份字段的值转换为列。

 

 

    通过上面几个例题我们可以看到,对于简单的行列转置问题,无论是 SQL 还是 SPL 都可以很好的解决。但是实际应用中的转置不全是简单的分组汇总 +PIVOT 这么简单,在后续的文章中,我们将为大家介绍 SQL 和 SPL 是如何解决复杂静态转置和动态转置的。

Scores.csv

Olympic.txt

MonthSales.csv

SQL 与 SPL 对比系列:
SQL 和 SPL 的集合运算对比
SQL 和 SPL 的选出运算对比
SQL 和 SPL 的有序运算对比
SQL 和 SPL 的等值分组对比
SQL 和 SPL 的非等值分组对比
SQL 和 SPL 的有序分组对比
SQL 和 SPL 的一对一和一对多连接对比
SQL 和 SPL 的多对一连接对比
SQL 和 SPL 的多对多连接对比
SQL 和 SPL 的基本静态转置对比
SQL 和 SPL 的复杂静态转置对比
SQL 和 SPL 的动态转置对比
SQL 和 SPL 的递归对比