详谈转置 pivot

问题概要

  在日常工作中时常会遇到将数据表的行列进行转换的问题。SQL 提供了静态转置的功能 pivot 和 unpivot,但适用范围很受限,要用 SQL 实现一些比较复杂的转置功能常常会遇到语句过于复杂的问题,而且缺少一个标准的解决思路。而集算器的 SPL 语言,则因其语法的灵活和函数库的丰富,可以完美地弥补 SQL 在这方面的不足。

  下面则通过实例详细阐述一下转置功能的实现。

基础篇

一、行转列

1、数据库的 pivot

  pivot 并不是从一开始就存在的功能,只有主流大数据库厂商较新版本产品,例如 Oralce 11g 以上或 SqlServer2005 以上,才支持这个功能。

  从名称中可以猜到,这个功能是实现行与列的转换,也就是将行中的值作为列名。但是,数据库的行、列,与普通的表格不一样,不能直接将 X 轴与 Y 轴相互对掉就算大功告成。究其原因,数据库的列是有唯一性的(也就是列名是不能重复的),而行中存储的是动态的数据,如果不作为主键,就是可以重复的。所以,pivot 的实际应用,基本都要跟随在分组聚合运算之后,通过分组把用于转置的列(通常都是维度)中每一行数据都处理成不重复的值后,再将各行的值作为列名来展开。

  从具体应用来看,pivot 的作用,其实就是将某一列的聚合结果,细分为多个更具体的列的聚合结果,以达到更直观的视觉效果。

  光说概念是不是比较枯燥,不容易理解?下面我们就以一个具体事例说明,比如 Oracle 数据库中有一个学生成绩表(StudentScore):

  如果想统计每个班的各科最高分,传统的做法是:

select CLASS,SUBJECT, max(SCORE) THE_HIGHEST_SCORE from STUDENTSCORE group by CLASS,SUBJECT

  上面的结果可以说观感非常不好:首先,在“班级”一列里,一班、二班重复出现,很容易就让人看错行;其次,在“科目”一列里,语文、数学和英语三个科目都放在一起,然而实际上这三个科目的最高分并没有什么比较的意义。

事实上,我们应该更希望看到以下这样的结果:

  这个结果中,把科目这一列中的三个科目,各自分离出来单独作为一列,既减少了无用的重复,又明确了各科目最高分之间的相互独立性,看上去清晰明了了很多。

  可以说,pivot 就是为了这个目的而诞生的,为了实现上面的结果,现在的查询写法如下:

select * from (select CLASS, SUBJECT, SCORE from STUDENTSCORE) pivot (max( SCORE) for SUBJECT in ('Maths' as MAX_MATHS, 'English' as MAX_ENGLISH, 'Chinese' asMAX_CHINSES))

2、集算器的 pivot

  有的同学可能会问,既然数据库中已经有了 pivot,那为什么我还需要集算器的 pivot 呢?

  答案是:首先,不是所有的数据库都提供 pivot;其次,就算所有的数据库都提供 pivot,但如果是汇总了多个数据库的数据后还想再来个 pivot?那还是要用到集算器的 pivot。

  下面我们来看集算器的 pivot 如何使用

代码说明:

A1:第一步连接数据库

A2:第二步提取数据做预处理 (这一步可进一步扩展为做汇总或聚合等复杂的计算,具体方法请参考相关文章)

A3:第三步即实现 pivot 的列转行功能并呈现出来,其效果与 Oracle 的 pivot 是完全一样的。

3、pivot 的其他意义

  除了数据呈现需求,将行转为列后,还可以使用列间的计算方法。因为列与行的属性不同,有些列间的计算要在行间实现会比较繁琐。比如学校对班级成绩的某种考核评比,数、外、语三科的权重分别是:0.6、0.3 和 0.1,用两个班的三科平均分来计算评比指标:

计算结果:

  上面的计算,假如要在行间实现,则会麻烦许多,有兴趣的同学可以自己试一下。

二、 列转行

1、数据库的 unpivot

  有行转列,自然就有列转行。还是以 Oracle 为例,它提供的列转行函数是 unpivot。

  列转行的功能在业务上又有什么意义呢?我们来看这样一份个人成绩表(PersonalScore):

  如果想知道的是每个人最擅长哪个科目(也就是每个人的哪一科得分最高),行间计算时用 max 函数会很方便,而使用列间计算则相对比较繁琐。这时 unpivot 函数就派上用场了:

with T1 as (select * from PERSONALSCORE unpivot (SCORE for SUBJECT in(MATHS,ENGLISH, CHINESE))),

T2

as (select NAME NAME, max(SCORE) The_Highest_Score

from T1 group by NAME)

selectT1.NAMENAME, T1.SUBJECT Good_Subject, T2.The_Highest_Score Good_Score_Score

from T1 joinT2

onT1.NAME= T2.NAMEandT1.SCORE =T2.The_Highest_Score

2、集算器的 pivot@r

  那么,如果使用的数据库不是 Oracle 怎么办?还需要研究新数据库的转置语法细节么?如果数据库不支持转置语句又怎么办?需要用 case when 或是子查询之类的来间接实现类似功能么?

  不必如此烦恼!因为我们有集算器:

计算结果,二者是一样的(在排序上可能略有差异):

  另外,还需要注意一点:数据库的 unpivot 并不完全是 pivot 的逆运算,因为 pivot 语句中往往包含了聚合函数,而聚合计算本身是不可逆的,也就是说 unpivot 并不能将 pivot 聚合后的结果再还原回原先的详细数据。但是集算器的 pivot 因为并不参与聚合计算(聚合计算在 pivot 执行之前已经单独执行了),所以集算器的 pivot@r 可以说是集算器的 pivot 运算的逆运算。

高级篇

一、 双向转置

  有时需要一些更复杂的转置操作,比如有这样一个学生成绩表(Score)

  而我们想要得到类似下面结构的学生成绩表(含义是查看某个学生某科目的成绩变化趋势):

  这里,首先要将数学、语文等列合并成科目列,需要列转行的操作;而要将学期列拆分成学期一、学期二等列,需要行转列的操作。

  考虑到数据表的结构一般是行数远大于列数,所以我们可以先进行列转行,再进行行转列。由于本表的原始数据在行列转换后数据与转换前的表中数据可以一一对应(不需要计算聚合),因此使用集算器的 pivot@r 和 pivot 函数显然会更方便。

运行结果:

二、 动态列转置

  上面举的例子都属于静态转置,要求处理的表格和数据都是“规规矩矩”的。但实际业务中却总有不那么守规矩的异类存在,而且相信数量还不少,这时用 SQL 不管是 pivot/unpivot、还是 case when,还是别的啥,都有点力不从心……那该怎么办?这时集算器的优势就体现出来了:

  比如有下面一个记录收入情况的个人收入表(PersonalIncome)

  但我们想得到一个类似下面结构的表

MANE INCOME_SOURCE_1 INCOMR_AMOUNT_1 INCOME_SOURCE_2 INCOMR_AMOUNT_2 ……

Zhangsan Wages 8000 Stock 6000 ……

  我们不确定行转列后,列的数量,甚至连列名也不能完全确定。这时就不能使用只适用于静态转置的 pivot 函数了,而需要使用动态转置的方法。而集算器的 SPL 语言在动态编程方面,要远比 SQL 语言灵活得多:

结果如下:

三、 转置同时列间计算

  假设我有一张关于蔬菜的一周价格清单

  而我想由此计算得出关于各种蔬菜的一周价格走势,其中走势又包含四种状态:上涨、下降、平稳和初始(周一的值)。

  设计出来的表结构大体如下

VEGETABLES Monday Tuesday Wednesday Thursday ……

Eggplant Initial Rise Decline Rise ……

Cucumber Initial Rise Rise ……

……

  虽然需要使用的转置属于静态类型,但在转置时需要实现列间的计算,这种计算对于 SQL 来说,处理起来非常麻烦。但若使用灵活性更强的集算器的 SPL 语言,则会轻松许多:

获得“蔬菜的一周价格走势”表如下

总结

  相比于 SQL 提供的 pivot 和 unpivot,集算器 SPL 语言所提供的转置功能要更加灵活,适应性也更加广泛,可以满足各种复杂的转置需求。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容