背景

在电子表格Excel中的数据透视表,能够快速汇总列表中的数据,能把很多行的流水数据表格变成二维汇总表格,然后使用 PowerQuery ,再变成流水数据表格。

KingbaseES 数据库中,使用SQL查询语句,同样实现数据在流水与汇总样式之间转换。

行转列

数据统计工作中,有一种报表是需要横向展示某个维度的数值,以维度值作为列名,配合另一个或几个维度形成二维报表。

行转列的需求,通常是数据的压缩,实现数据提升维度,比如使用二维表格来统计显示一维数据。其特点是,输入的一维数据的结构是已知的固定的,输出的二维数据的结构是未知的动态的,所以需要在查询语句中,显性说明其结构。

数据准备

准备一个学生课程的成绩单数据系统

  1. drop view if exists testscore_view ;
  2. create or replace view testscore_view
  3. (year, student, subject, testscore) as
  4. values (2020, '张三', '语文', 76),
  5. (2020, '张三', '化学', 83),
  6. (2020, '张三', '物理', 69),
  7. (2020, '张三', '数学', 49),
  8. (2020, '张三', '英语', 63),
  9. (2020, '李四', '语文', 48),
  10. (2020, '李四', '化学', 48),
  11. (2020, '李四', '物理', 77),
  12. (2020, '李四', '英语', 38),
  13. (2020, '王五', '语文', 88),
  14. (2020, '王五', '物理', 45),
  15. (2020, '王五', '数学', 92),
  16. (2020, '王五', '英语', 56),
  17. (2021, '张三', '语文', 55),
  18. (2021, '张三', '化学', 73),
  19. (2021, '张三', '物理', 87),
  20. (2021, '张三', '数学', 41),
  21. (2021, '张三', '英语', 52),
  22. (2021, '李四', '语文', 87),
  23. (2021, '李四', '化学', 91),
  24. (2021, '李四', '物理', 39),
  25. (2021, '李四', '英语', 54),
  26. (2021, '王五', '语文', 80),
  27. (2021, '王五', '物理', 70),
  28. (2021, '王五', '数学', 46),
  29. (2021, '王五', '英语', 29);
  30. drop view if exists student_view;
  31. create view student_view as
  32. select *
  33. from (values (1, '张三'), (2, '李四'), (3, '王五')) std(sn, student);
  34. drop view if exists subject_view;
  35. create view subject_view as
  36. select *
  37. from (values (1, '语文'), (2, '化学'), (3, '物理'), (4, '数学'), (5, '英语')) std(sn, subject);
  38. drop view if exists year_view;
  39. create view year_view as
  40. select *
  41. from (values (2020), (2021), (2022)) std(year);

分组聚合函数+CASE

通常在SQL的select list中,使用CASE语句,建立二维报表。

  1. select student,
  2. avg(case when subject = '语文' then testscore end) as "语文",
  3. avg(case when subject = '化学' then testscore end) as "化学",
  4. avg(case when subject = '物理' then testscore end) as "物理",
  5. avg(case when subject = '数学' then testscore end) as "数学",
  6. avg(case when subject = '英语' then testscore end) as "英语"
  7. from testscore_view
  8. group by student
  9. student | 语文 | 化学 | 物理 | 数学 | 英语
  10. ---------+------+------+------+------+------
  11. 张三 | 66 | 78 | 78 | 45 | 58
  12. 王五 | 84 | | 58 | 69 | 43
  13. 李四 | 68 | 70 | 58 | | 46
  14. (3 行记录)

根据压缩数据的格式,横向展开数据列选取不同方式

数据压缩合并成某种格式数据,然后根据横向维度值,展开成为数据列。

  • 字符分割

要求字符拼接与分割的位置与顺序一致,避免空值造成错位

  1. select student,
  2. split_part(split_part(subscr_spl, ',', 1), ':', 2) as "语文",
  3. split_part(split_part(subscr_spl, ',', 2), ':', 2) as "化学",
  4. split_part(split_part(subscr_spl, ',', 3), ':', 2) as "物理",
  5. split_part(split_part(subscr_spl, ',', 4), ':', 2) as "数学",
  6. split_part(split_part(subscr_spl, ',', 5), ':', 2) as "英语"
  7. from (select student, string_agg(subject || ':' || val, ',') as subscr_spl
  8. from (select student_view.student,
  9. subject_view.subject,
  10. avg(testscore)::numeric(10, 2) val
  11. from (student_view cross join subject_view)
  12. left join testscore_view using (student, subject)
  13. group by student_view.student, subject_view.subject
  14. order by min(student_view.sn), min(subject_view.sn)) std
  15. group by student
  16. ) as t;
  • 数组

要求数组生成与取值的位置与顺序一致,避免空值造成错位

  1. select student,
  2. subscr_arr[1] as "语文",
  3. subscr_arr[2] as "化学",
  4. subscr_arr[3] as "物理",
  5. subscr_arr[4] as "数学",
  6. subscr_arr[5] as "英语"
  7. from (select student, array_agg(val) as subscr_arr
  8. from (select student_view.student,
  9. subject_view.subject,
  10. avg(testscore)::numeric(10, 2) val
  11. from (student_view cross join subject_view)
  12. left join testscore_view using (student, subject)
  13. group by student_view.student, subject_view.subject
  14. order by min(student_view.sn), min(subject_view.sn)) std
  15. group by student
  16. ) as t;
  • JSON数据格式
  1. select student,
  2. subscr_json ->> '语文' as "语文",
  3. subscr_json ->> '化学' as "化学",
  4. subscr_json ->> '物理' as "物理",
  5. subscr_json ->> '数学' as "数学",
  6. subscr_json ->> '英语' as "英语"
  7. from (select student, json_object_agg(subject, val) as subscr_json
  8. from (select student, subject, avg(testscore)::numeric(10, 2) val
  9. from testscore_view
  10. group by student, subject) std
  11. group by student) std ;

crosstab函数

扩展 tablefunc 中的 crosstab 函数,用来生成pivot 展示,即通过横向而不是下拉展示。

  1. create extension tablefunc;
  2. SELECT *
  3. FROM crosstab($query$ select student, subject, avg(testscore) value
  4. from testscore_view
  5. group by student, subject
  6. order by 1,2 $query$,
  7. $column$ select subject from subject_view $column$)
  8. AS ct(student text,
  9. "语文" numeric(10, 2),
  10. "化学" numeric(10, 2),
  11. "物理" numeric(10, 2),
  12. "数学" numeric(10, 2),
  13. "英语" numeric(10, 2));

PIVOT 操作符

PIVOT 通过一种新的操作符以交叉表格式显示任何查询,可以满足纵向多列的表格样式。

  • pivot(聚合函数 for 列名 in (类型)),其中 in ('') 中可以指定列名,还可以指定子查询
  • pivot(任一聚合函数 for 需转为列的值所在列名 in (需转为列名的值))
  1. create extension kdb_utils_function;
  2. select *
  3. from (select student, subject, testscore from testscore_view)
  4. pivot (
  5. avg(testscore) for subject in (
  6. '语文' as "语文",
  7. '化学' as "化学",
  8. '物理' as "物理",
  9. '数学' as "数学",
  10. '英语' as "英语" )) ;
  11. student | 语文 | 化学 | 物理 | 数学 | 英语
  12. ---------+---------------------+---------------------+---------------------+---------------------+---------------------
  13. 李四 | 67.5000000000000000 | 69.5000000000000000 | 58.0000000000000000 | | 46.0000000000000000
  14. 王五 | 84.0000000000000000 | | 57.5000000000000000 | 69.0000000000000000 | 42.5000000000000000
  15. 张三 | 65.5000000000000000 | 78.0000000000000000 | 78.0000000000000000 | 45.0000000000000000 | 57.5000000000000000
  16. (3 行记录)
  17. select *
  18. from (select year, student, subject, testscore from testscore_view)
  19. pivot (
  20. avg(testscore) for subject in (
  21. '语文' as "语文",
  22. '化学' as "化学",
  23. '物理' as "物理",
  24. '数学' as "数学",
  25. '英语' as "英语" )) ;
  26. year | student | 语文 | 化学 | 物理 | 数学 | 英语
  27. ------+---------+---------------------+---------------------+---------------------+---------------------+---------------------
  28. 2020 | 李四 | 48.0000000000000000 | 48.0000000000000000 | 77.0000000000000000 | | 38.0000000000000000
  29. 2020 | 王五 | 88.0000000000000000 | | 45.0000000000000000 | 92.0000000000000000 | 56.0000000000000000
  30. 2020 | 张三 | 76.0000000000000000 | 83.0000000000000000 | 69.0000000000000000 | 49.0000000000000000 | 63.0000000000000000
  31. 2021 | 李四 | 87.0000000000000000 | 91.0000000000000000 | 39.0000000000000000 | | 54.0000000000000000
  32. 2021 | 王五 | 80.0000000000000000 | | 70.0000000000000000 | 46.0000000000000000 | 29.0000000000000000
  33. 2021 | 张三 | 55.0000000000000000 | 73.0000000000000000 | 87.0000000000000000 | 41.0000000000000000 | 52.0000000000000000
  34. (6 行记录)

PIVOT 操作符的限制

PIVOT 操作符是根据明确的数据集合类型进行运算,需要避免以下使用方式

  • FROM子句的子查询,包含 * 号。
  1. select *
  2. from ( select * from testscore_view)
  3. pivot ( ...
  4. ;
  • FROM子句的子查询,定义别名
  1. select *
  2. from (select student, subject, testscore from testscore_view) as stdsrc
  3. pivot ( ...
  4. ;
  • 使用 CTE 代替FROM子句的子查询
  1. with stdsrc as (select student, subject, testscore from testscore_view)
  2. select *
  3. from stdsrc
  4. pivot ( ...
  5. ;
  • FROM子句的子查询,在select子句和from子句中,包含函数或子查询等表达式
  1. select *
  2. from (select student, subject, SQRT(testscore)*10 testscore from testscore_view )
  3. pivot ( ... ;
  4. select *
  5. from (select student, subject, testscore from (select student, subject, testscore from testscore_view ))
  6. pivot ( ... ;

上述被限制使用的查询,可以创建成视图,便可以进行 PIVOT 操作符运算。

工具 ksql 的元命令 \crosstabview

\crosstabview [ colV [ colH [ colD [ sortcolH ] ] ] ]

执行当前的查询缓冲区(像\g那样)并且在一个交叉表格子中显示结果。该查询必须返回至少三列。由colV标识的输出列会成为垂直页眉并且colH所标识的输出列会成为水平页眉。colD标识显示在格子中的输出列。sortcolH标识用于水平页眉的可选的排序列。

  1. select student, subject, avg(testscore)::numeric(10,2) testscore
  2. from testscore_view
  3. group by student, subject
  4. \crosstabview subject student testscore
  5. subject | 李四 | 王五 | 张三
  6. ---------+-------+-------+-------
  7. 语文 | 67.50 | 84.00 | 65.50
  8. 物理 | 58.00 | 57.50 | 78.00
  9. 英语 | 46.00 | 42.50 | 57.50
  10. 数学 | | 69.00 | 45.00
  11. 化学 | 69.50 | | 78.00
  12. (5 行记录)

列转行

数据操作工作中,将报表格式数据导入数据表,是需要以横向列作为维度值。

列转行的需求,通常是数据的膨胀,实现数据降低维度,比如使用一维数据表存储二维表格的单元数据。其特点是,输入的二维数据的结构是未知的动态的,输出的一维数据的结构是已知的静态的,所以需要在查询语句中,显性说明其结构。

数据准备

  1. create or replace view stdscore_view
  2. (year, student, "语文", "化学", "物理", "数学", "英语")
  3. as
  4. values (2020, '李四', 48, 48, 77, null, 38),
  5. (2020, '王五', 88, null, 45, 92, 56),
  6. (2020, '张三', 76, 83, 69, 49, 63),
  7. (2021, '李四', 87, 91, 39, null, 54),
  8. (2021, '王五', 80, null, 70, 46, 29),
  9. (2021, '张三', 55, 73, 87, 41, 52) ;

union all

  1. with stdscore (year, student, subject, testscore) as
  2. (select year, student, '语文', "语文"
  3. from stdscore_view
  4. union all
  5. select year, student, '化学', "化学"
  6. from stdscore_view
  7. union all
  8. select year, student, '物理', "物理"
  9. from stdscore_view
  10. union all
  11. select year, student, '数学', "数学"
  12. from stdscore_view
  13. union all
  14. select year, student, '英语', "英语"
  15. from stdscore_view
  16. )
  17. select *
  18. from stdscore
  19. where testscore is not null ;

CASE

  1. select year,
  2. student,
  3. subject,
  4. case subject
  5. when '语文' then "语文"
  6. when '化学' then "化学"
  7. when '物理' then "物理"
  8. when '数学' then "数学"
  9. when '英语' then "英语"
  10. end as testscore
  11. from stdscore_view, subject_view
  12. where testscore is not null ;

UNPIVOT 操作符

UNPIVOT 操作符可以看作 PIVOT 操作符的反向运算,根据多个列合并为新维度列,列值作为新数据行而合并到指定列。

  1. select year,
  2. student,
  3. subject,
  4. testscore
  5. from stdscore_view
  6. unpivot (testscore for subject in
  7. ( '语文' as "语文", '化学' as "化学", '物理' as "物理", '数学' as "数学", '英语' as "英语" ));

总结

当数据需要变形是,Pivot 为 SQL 语言增添了一个非常重要且实用的功能。您可以使用 pivot 函数针对任何关系表创建一个交叉表报表,而不必编写包含大量 decode 函数的令人费解的、不直观的代码。同样,您可以使用 unpivot 操作转换任何交叉表报表,以常规关系表的形式对其进行存储。

KingbaseES 的行列转换的更多相关文章

  1. KingbaseES 行列转换函数

    关键字:    行专列,列转行, pivot, unpivot 行列转换是在数据分析中经常用到的一项功能,KingbaseES从V8R6C3B0071版本开始通过扩展插件(kdb_utils_func ...

  2. Oracle学习之路-- 案例分析实现行列转换的几种方式

    注:本文使用的数据库表为oracle自带scott用户下的emp,dept等表结构. 通过一个例子来说明行列转换: 需求:查询每个部门中各个职位的总工资 按我们最原始的思路可能会这么写:       ...

  3. SQL Server中行列转换 Pivot UnPivot

    SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PI ...

  4. SQL(横表和纵表)行列转换,PIVOT与UNPIVOT的区别和使用方法举例,合并列的例子

    使用过SQL Server 2000的人都知道,要想实现行列转换,必须综合利用聚合函数和动态SQL,具体实现起来需要一定的技巧,而在SQL Server 2005中,使用新引进的关键字PIVOT/UN ...

  5. 如何用Pivot实现行列转换

    在Oracle中,如果要实现行列转换,较为常见的是用DECODE和CASE语句.对于简单的行列转行,DECODE和CASE语句尚能应付.在逻辑比较复杂,分组聚合较多的场景中,DECODE和CASE语句 ...

  6. SQL Server SQL性能优化之--pivot行列转换减少扫描计数优化查询语句

    原文出处:http://www.cnblogs.com/wy123/p/5933734.html 先看常用的一种表结构设计方式: 那么可能会遇到一种典型的查询方式,主子表关联,查询子表中的某些(或者全 ...

  7. SQL SERVER 合并重复行,行列转换

    引用自:http://www.cnblogs.com/love-summer/archive/2012/03/27/2419778.html sql server2000 里面如何实现oracle10 ...

  8. SQL中PIVOT 行列转换

    来源:http://www.studyofnet.com/news/295.html PIVOT通过将表达式某一列中的唯一值转换为输出中的多个列来旋转表值表达式,并在必要时对最终输出中所需的任何其余列 ...

  9. 在Sqlserver下巧用行列转换日期的数据统计

    在Sqlserver下巧用行列转换日期的数据统计 前言 在SQLSERVER 中有很多统计函数的基础语法,有使用Group By 或 partition by 后配合Sum,Count(*) 等用法. ...

随机推荐

  1. python基础教程:__call__用法

    __call__可以使得方法变成可被调用对象:(PS:python中的方法和普通函数有点区别:方法的第一个参数是类实例) 允许一个类的实例像函数一样被调用.实质上说,这意味着 x() 与 x.call ...

  2. Map接口总结(如何使用默认方法)

    Map接口总结(如何使用默认方法) Map的基本使用 默认方法的问题,有什么坑 常用的默认方法应用场景 基本操作 get put(区别:Collection接口中添加为set) putAll remo ...

  3. 密码学系列之:使用openssl检测网站是否支持ocsp

    目录 简介 支持OCSP stapling的网站 获取服务器的证书 获取OCSP responder地址 发送OCSP请求 一个更加简单的方法 总结 简介 OCSP在线证书状态协议是为了替换CRL而提 ...

  4. 常用Linux音译

    su:Swith user 切换用户,切换到root用户 cat: Concatenate 串联 uname: Unix name 系统名称 df: Disk free 空余硬盘 du: Disk u ...

  5. web 前端 基础HTML知识点

    web系统架构体系 B/S(Browser/Server):浏览器实现 优点: 规范.使用方便.本身实现成本低 容易升级.便于维护 缺点: 没有网络,无法使用 保存数据量有限,和服务器交互频率高.耗费 ...

  6. 08 MySQL_SQL_DQL_select数据查询条件判断

    导入*.sql数据到数据库 windows系统 ​ source d:/tables.sql; Linux系统 source /home/soft/桌面/tables.sql; 导入完成后 测试查询 ...

  7. intellidea 快捷键-*01

    快捷键: 0.竖向选择文本: alt+shift+insert:https://www.cnblogs.com/JonaLin/p/11422110.html 如果想修改快捷键(setting-> ...

  8. (一)java基础篇-----认识java

    1.简单介绍java起源: 1995年,詹姆斯-高斯林在sun公司开发出java编程语言.到2010年,sun公司被Oracle公司收购,而詹姆斯-高斯林也离开了Oracle公司.所以,如今想要安转j ...

  9. DQL分组查询和DQL分页查询

    分组查询: 1.语法:group by 分组字段: 2.注意: 分组之后查询的字符按:分组字段.聚合函数 where 和having 的区别 where再分组前进行限定,如果不满足条件则不参与分组.h ...

  10. Element 2 组件源码剖析之布局容器

    0x00 简介 前文分析过组件的 布局栅格化(Grid Layout) ,通过基础的 24 分栏,迅速简便地创建布局. 本文将介绍用于布局的容器组件,使用 Flexbox 功能将其所控制区域设定为特定 ...