Apache Doris 行列转换可以这样玩

news2024/11/27 21:55:33

行列转换在做报表分析时还是经常会遇到的,今天就说一下如何实现行列转换吧。

行列转换就是如下图所示两种展示形式的互相转换

1. 行转列

我们来看一个简单的例子,我们要把下面这个表的数据,转换成图二的样式

image-20230914151818953.png

要转换的结果数据展示

image-20230914152642915.png

先看看建表语句:

CREATE TABLE tb_score_01(
 id INT(11) NOT NULL,
 userid VARCHAR(20) NOT NULL COMMENT '用户id',
 subject VARCHAR(20) COMMENT '科目',
 score DOUBLE COMMENT '成绩'
)
DUPLICATE KEY(`id`)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(`id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
"in_memory" = "false",
"storage_format" = "V2",
"light_schema_change" = "true",
"disable_auto_compaction" = "false"
);

INSERT INTO tb_score_01  VALUES (1,'001','语文',90);
INSERT INTO tb_score_01  VALUES (2,'001','数学',92);
INSERT INTO tb_score_01  VALUES (3,'001','英语',80);
INSERT INTO tb_score_01  VALUES (4,'002','语文',88);
INSERT INTO tb_score_01  VALUES (5,'002','数学',90);
INSERT INTO tb_score_01  VALUES (6,'002','英语',75.5);
INSERT INTO tb_score_01  VALUES (7,'003','语文',70);
INSERT INTO tb_score_01  VALUES (8,'003','数学',85);
INSERT INTO tb_score_01  VALUES (9,'003','英语',90);
INSERT INTO tb_score_01  VALUES (10,'003','政治',82);

传统的做法我们大概是这样实现,一般是通过 case when 语句

SELECT userid,
SUM(CASE `subject` WHEN '语文' THEN score ELSE 0 END) as '语文',
SUM(CASE `subject` WHEN '数学' THEN score ELSE 0 END) as '数学',
SUM(CASE `subject` WHEN '英语' THEN score ELSE 0 END) as '英语',
SUM(CASE `subject` WHEN '政治' THEN score ELSE 0 END) as '政治' 
FROM tb_score 
GROUP BY userid;

或者

SELECT userid,
SUM(IF(`subject`='语文',score,0)) as '语文',
SUM(IF(`subject`='数学',score,0)) as '数学',
SUM(IF(`subject`='英语',score,0)) as '英语',
SUM(IF(`subject`='政治',score,0)) as '政治' 
FROM tb_score 
GROUP BY userid;

我们来看看 Doris 怎么实现这个行转列呢,有没有更简单、性能更好的一种方式

  1. 我们是不是可以首先将这个科目、成绩组成一个Map

  2. 然后在外层对这个 Map 进行遍历展开

  3. 从而完成这样一个行列转换呢

我们来看看实现

select 
 userid,
 IFNULL(map['语文'],0) as '语文',
 IFNULL(map['英语'],0) as '英语',
 IFNULL(map['数学'],0) as '数学',
 IFNULL(map['政治'],0) as '政治'
from  (
 select userid ,map_agg(subject,score) as map from tb_score group by userid
) t ;

这样实现上性能更好,我们来看一下效果

select
 ->     userid,
 ->     IFNULL(map['语文'],0) as '语文',
 ->     IFNULL(map['英语'],0) as '英语',
 ->     IFNULL(map['数学'],0) as '数学',
 ->     IFNULL(map['政治'],0) as '政治'
 -> from  (
 ->     select userid ,map_agg(subject,score) as map from tb_score group by userid
 -> ) t ;
+--------+--------+--------+--------+--------+
| userid | 语文   | 英语   | 数学   | 政治   |
+--------+--------+--------+--------+--------+
| 001    |     90 |     80 |     92 |      0 |
| 002    |     88 |   75.5 |     90 |      0 |
| 003    |     70 |     90 |     85 |     82 |
+--------+--------+--------+--------+--------+
3 rows in set (0.02 sec)

2. 列转行

实际使用中我们还有很多场景要把数据冲列转成行,下面我们来看一个例子,这个例子中每行是一个学生的,语文、数学、英语、政治的成绩,

image-20230914152642915.png

我们想转换成每门成绩都是独立的一行,转出的效果如下:

image-20230914152846996.png

我们来看看一个宽表转成高表我们之前的是怎么实现,一般我们是通过union all的方式,每科我们都是一个单独的SQL语句,然后将这些SQL Unoin all 在一起得到我们想要的结果。

SELECT userid,'语文' AS course,cn_score AS score FROM tb_score1
UNION ALL
SELECT userid,'数学' AS course,math_score AS score FROM tb_score1
UNION ALL
SELECT userid,'英语' AS course,en_score AS score FROM tb_score1
UNION ALL
SELECT userid,'政治' AS course,po_score AS score FROM tb_score1
ORDER BY userid;

这样做的缺点:

  1. SQL 冗余

  2. 大量的union all 也会带来性能问题

我们来看看 Doris 怎么实现,首先 Doris 提供了 Lateral view,其实就是用来和像类似explode这种UDTF函数联用的,lateral view会将 UDTF 生成的结果放到一个虚拟表中,然后这个虚拟表会和输入行进行 join来达到连接 UDTF 外的 select 字段的目的

还是以上面的例子来看,Doris我怎么对这个宽表转成高表,实现就是借助Lateral view

CREATE TABLE `tb_score1` (
 `id` int(11) NOT NULL,
 `userid` varchar(20) NOT NULL COMMENT '用户id',
 `cn_score` double NULL COMMENT '语文成绩',
 `math_score` double NULL COMMENT '数学成绩',
 `en_score` double NULL COMMENT '英语成绩',
 `po_score` double NULL COMMENT '政治成绩'
) ENGINE=OLAP
UNIQUE KEY(`id`)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(`id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
"is_being_synced" = "false",
"storage_format" = "V2",
"light_schema_change" = "true",
"disable_auto_compaction" = "false",
"enable_single_replica_compaction" = "false"
);;

INSERT INTO `tb_score1` (`id`, `userid`, `cn_score`, `math_score`, `en_score`, `po_score`) VALUES (1, '001', 90, 92, 80, 0);
INSERT INTO `tb_score1` (`id`, `userid`, `cn_score`, `math_score`, `en_score`, `po_score`) VALUES (2, '002', 88, 90, 75.5, 0);
INSERT INTO `tb_score1` (`id`, `userid`, `cn_score`, `math_score`, `en_score`, `po_score`) VALUES (3, '003', 70, 85, 90, 82);
  1. 首先我借助Lateral view 形成一个 UserID、客户成绩组成一个字符(使用逗号连接),达到下面的效果
+--------+--------------------+
| userid | arr                |
+--------+--------------------+
| 001    | ["语文", "90"]     |
| 001    | ["数学", "92"]     |
| 001    | ["英语", "80"]     |
| 001    | ["政治", "0"]      |
| 002    | ["语文", "88"]     |
| 002    | ["数学", "90"]     |
| 002    | ["英语", "75.5"]   |
| 002    | ["政治", "0"]      |
| 003    | ["语文", "70"]     |
| 003    | ["数学", "85"]     |
| 003    | ["英语", "90"]     |
| 003    | ["政治", "82"]     |
+--------+--------------------+
12 rows in set (0.02 sec)
  1. 然后对这个上面的 arr 字符串,借助于 Doris 提供的 SPLIT_BY_STRING 函数完成字符串转数组的动作

  2. 最后遍历数组

  3. 完成列转行的效果

SELECT
 userid,
 element_at ( arr, 1 ) AS SUBJECT,
 element_at ( arr, 2 ) AS score 
FROM
 (
 SELECT
 userid,
 SPLIT_BY_STRING ( sub, ',' ) arr 
 FROM
 (
 SELECT
 userid,
 array (
 concat( '语文', ',', cn_score ),
 concat( '数学', ',', math_score ),
 concat( '英语', ',', en_score ),
 concat( '政治', ',', po_score )) AS scores 
 FROM
 tb_score1 
 ) t LATERAL VIEW explode ( scores ) tbl1 AS sub 
 ) aaa

最后的效果如下:

SELECT
 ->         userid,
 ->         element_at ( arr, 1 ) AS SUBJECT,
 ->         element_at ( arr, 2 ) AS score
 -> FROM
 ->         (
 ->         SELECT
 ->                 userid,
 ->                 SPLIT_BY_STRING ( sub, ',' ) arr
 ->         FROM
 ->                 (
 ->                 SELECT
 ->                         userid,
 ->                         array (
 ->                                 concat( '语文', ',', cn_score ),
 ->                                 concat( '数学', ',', math_score ),
 ->                                 concat( '英语', ',', en_score ),
 ->                         concat( '政治', ',', po_score )) AS scores
 ->                 FROM
 ->                         tb_score1
 ->                 ) t LATERAL VIEW explode ( scores ) tbl1 AS sub
 ->         ) aaa;
+--------+---------+-------+
| userid | SUBJECT | score |
+--------+---------+-------+
| 001    | 语文    | 90    |
| 001    | 数学    | 92    |
| 001    | 英语    | 80    |
| 001    | 政治    | 0     |
| 002    | 语文    | 88    |
| 002    | 数学    | 90    |
| 002    | 英语    | 75.5  |
| 002    | 政治    | 0     |
| 003    | 语文    | 70    |
| 003    | 数学    | 85    |
| 003    | 英语    | 90    |
| 003    | 政治    | 82    |
+--------+---------+-------+
12 rows in set (0.02 sec)

日记本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1042186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络补充

未分类文档 CDMA是码分多路复用技术 和CMSA不是一个东西 UPD是只确保发送 但是接收端收到之后(使用检验和校验 除了检验的部分相加 对比检验和是否相等。如果不相同就丢弃。 复用和分用是发生在上层和下层的问题。通过比如时分多路复用 频分多路复用等。TCP IP 应用层的IO多路…

Coupang新手教程,Coupang怎么收款?——站斧浏览器

coupang新手教程 韩国coupang入驻条件很简单,只需要你提供注册四件套就可以了; Coupang的经营模式呢可以说和我们国内的电商比较像; 前期可以做无货源模式;在熟悉平台一段时间后,可以去打造我们自己的精品店铺&…

start()方法源码分析

当我们创建好一个线程之后,可以调用.start()方法进行启动,start()方法的内部其实是调用本地的start0()方法, 其实Thread.java这个类中的方法在底层的Thread.c文件中都是一一对应的,在Thread.c中start0方法的底层调用了jvm.cpp文件…

Python机器学习实战-特征重要性分析方法(4):相关性分析(附源码和实现效果)

实现功能 计算各特征与目标变量之间的相关性。相关性越高的特征越重要。 实现代码 import pandas as pd from sklearn.datasets import load_breast_cancer import matplotlib.pyplot as pltX, y load_breast_cancer(return_X_yTrue) df pd.DataFrame(X, columnsrange(30)…

TLS/SSL(八) 基于ECC椭圆曲线的ECDH协议

一 ECC椭圆曲线的特性 ① DH密钥交换两个问题 1、计算过程含有大量的乘法,运算速度比较慢2、安全性基于大因数分解比较困难,需要比较长的密钥位数说明: 互联网主要使用ECDH的密钥交换协议,是基于DH密钥交换协议的升级备注: ECDH基于ECC椭圆曲线的原…

【咕咕送书第二期】| 计算机网络对于考研的重要性?

🎬 鸽芷咕:个人主页 🔥 个人专栏:《粉丝福利》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活! 文章目录 📋 前言什么是计算机网络?01 为什么计算机专业要学计算机网络02 计算机网络对考研的重要性 …

【二叉树】——链式结构(快速掌握递归与刷题技巧)

📙作者简介: 清水加冰,目前大二在读,正在学习C/C、Python、操作系统、数据库等。 📘相关专栏:C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d…

通过Python爬虫代理IP快速增加博客阅读量

前言 Python爬虫代理IP可以让你快速增加博客阅读量,因为它可以让你绕过一些反爬虫限制。本文将分享如何使用Python爬虫代理IP,以及如何使用它们来获取更多的博客阅读量。 一. 什么是代理IP 代理IP是一种在网络环境下使用的IP地址,它可以隐藏…

Renderbus瑞云渲染现在支持3dsMax 2024了

Autodesk于今年3月份发布了最新版本的3ds Max软件,其中包含令人兴奋的新功能和增强功能。3ds Max 2024渲染功能暂时可与V-Ray 6.1.2和corona 10配合使用,直至更多渲染引擎发布更新。而您现在可以在Renderbus瑞云渲染平台使用最新版本的3ds Max 2024。 关…

MybatisPlus两表关联查询MyBatis-Plus-Join

MybatisPlus两表关联查询MyBatis-Plus-Join 介绍 首先,让我们先了解一下MyBatis和MyBatis-Plus。MyBatis是一个流行的Java持久层框架,它提供了一种简单而强大的方式来将数据库操作与Java代码进行绑定。而MyBatis-Plus是基于MyBatis的增强工具&#xff0…

【异常报错】must call Vue.use(Vuex)

这个错误应该是在创建Vuex中出现的 把你main.js中的Vue.use(Vuex)写到store中,这里我的store/index.js中,即完美解决 其实仔细想想也可以发现,import就把整个文件给引入了,而index.js中有创建Store的实例,而在这时我们还没有Vue.…

【JAVA进阶】多线程

📃个人主页:个人主页 🔥系列专栏:JAVASE基础 前言: 什么是线程? 线程(thread)是一个程序内部的一条执行路径。 我们之前启动程序执行后,main方法的执行其实就是一条单独的执行路径。 public…

使用HHDBCS管理Redis

Redis是一款内存高速缓存数据库,可用于缓存,事件发布或订阅,高速队列等场景。 因此,根据需要,HHDBCS在主页设置了“发布窗口”及“订阅窗口”。 1 连接redis 打开HHDBCS,在数据库类型中选择Redis&#…

MATLAB实战 | 粮食储仓的通风控制问题

粮食储仓的通风控制问题 01、应用实战 【例1】粮食储仓的通风控制问题。在粮食储备中,合适的湿度是保证粮食质量的前提。一般来说,若粮食水分的吸收和蒸发量相等,这个湿度称为平衡点湿度。只有实际湿度处于平衡点湿度以下,粮食质…

SpringMVC-请求与相应

一、环境准备 <dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope> //确定范围避免与tomcat冲突</de…

iOS-自定义Intent及ShortCut,能通过快捷指令唤醒APP并跳转到指定页面

1.Xcode->New->File->搜索Intent 2.在新建的intent文件中中New intent 3.完善资料&#xff0c;内容可自定义 4.创建Extension&#xff0c;file -> new -> target , 选择 Intents Extension 即可 创建完成后&#xff0c;在intents文件中勾选以下target&#xff0…

独步全球:为何TikTok成为创业者的首选平台?

在当今数字时代&#xff0c;社交媒体平台的崛起已经成为了创业者们开展业务、推广产品和建立品牌的关键方式。 然而&#xff0c;在众多社交媒体平台中&#xff0c;TikTok正以惊人的速度崭露头角&#xff0c;成为越来越多创业者的首选平台。本文将深入探讨为何TikTok如此吸引创…

双指数移动平均线DEMA指标公式,减少传统均线的滞后性

双指数移动平均线DEMA由Patrick Mulloy发明的&#xff0c;对指数移动平均线EMA进行了改进&#xff0c;用于减少传统均线的滞后性&#xff0c;于1994年发表在美国金融类月刊《Technical Analysis of Stocks & Commodities》。 双指数移动平均线DEMA是通过两个指数移动平均线…

MyBatis 执行流程分析

文章目录 1. MyBatis 执行流程概述2. MyBatis 配置文件详解3. Mappers 映射器 1. MyBatis 执行流程概述 上篇文章讲到 MyBatis入门 MyBatis 的基本入门案例我们实现了通过 MyBatis 去获取数据库的数据&#xff0c;那么他的基本流程如下&#xff1a; 第一步&#xff1a;是从配置…

linux 归档和压缩文件和目录

打包&#xff1a; tar 是 Unix 和 Linux 系统中非常常用的命令之一。它可以将多个文件和目录打包成一个归档文件&#xff0c;并且支持压缩和解压缩功能。 将文件或&#xff08;和&#xff09;目录打包成一个归档文件 tar -cvf Arithmetic_Ghost.tar file1 file2 directory/…