Apache Doris 行列轉(zhuǎn)換可以這樣玩

行列轉(zhuǎn)換在做報表分析時還是經(jīng)常會遇到的,今天就說一下如何實現(xiàn)行列轉(zhuǎn)換吧萍诱。

行列轉(zhuǎn)換就是如下圖所示兩種展示形式的互相轉(zhuǎn)換

1. 行轉(zhuǎn)列

我們來看一個簡單的例子,我們要把下面這個表的數(shù)據(jù),轉(zhuǎn)換成圖二的樣式

image-20230914151818953.png

要轉(zhuǎn)換的結(jié)果數(shù)據(jù)展示

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','數(shù)學',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','數(shù)學',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','數(shù)學',85);
INSERT INTO tb_score_01  VALUES (9,'003','英語',90);
INSERT INTO tb_score_01  VALUES (10,'003','政治',82);

傳統(tǒng)的做法我們大概是這樣實現(xiàn),一般是通過 case when 語句

SELECT userid,
SUM(CASE `subject` WHEN '語文' THEN score ELSE 0 END) as '語文',
SUM(CASE `subject` WHEN '數(shù)學' THEN score ELSE 0 END) as '數(shù)學',
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`='數(shù)學',score,0)) as '數(shù)學',
SUM(IF(`subject`='英語',score,0)) as '英語',
SUM(IF(`subject`='政治',score,0)) as '政治' 
FROM tb_score 
GROUP BY userid;

我們來看看 Doris 怎么實現(xiàn)這個行轉(zhuǎn)列呢券时,有沒有更簡單、性能更好的一種方式

  1. 我們是不是可以首先將這個科目伏伯、成績組成一個Map

  2. 然后在外層對這個 Map 進行遍歷展開

  3. 從而完成這樣一個行列轉(zhuǎn)換呢

我們來看看實現(xiàn)

select 
 userid,
 IFNULL(map['語文'],0) as '語文',
 IFNULL(map['英語'],0) as '英語',
 IFNULL(map['數(shù)學'],0) as '數(shù)學',
 IFNULL(map['政治'],0) as '政治'
from  (
 select userid ,map_agg(subject,score) as map from tb_score group by userid
) t ;

這樣實現(xiàn)上性能更好,我們來看一下效果

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

2. 列轉(zhuǎn)行

實際使用中我們還有很多場景要把數(shù)據(jù)沖列轉(zhuǎn)成行,下面我們來看一個例子捌袜,這個例子中每行是一個學生的说搅,語文、數(shù)學虏等、英語弄唧、政治的成績适肠,

image-20230914152642915.png

我們想轉(zhuǎn)換成每門成績都是獨立的一行,轉(zhuǎn)出的效果如下:

image-20230914152846996.png

我們來看看一個寬表轉(zhuǎn)成高表我們之前的是怎么實現(xiàn)候引,一般我們是通過union all的方式侯养,每科我們都是一個單獨的SQL語句,然后將這些SQL Unoin all 在一起得到我們想要的結(jié)果澄干。

SELECT userid,'語文' AS course,cn_score AS score FROM tb_score1
UNION ALL
SELECT userid,'數(shù)學' 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 怎么實現(xiàn)逛揩,首先 Doris 提供了 Lateral view,其實就是用來和像類似explode這種UDTF函數(shù)聯(lián)用的麸俘,lateral view會將 UDTF 生成的結(jié)果放到一個虛擬表中辩稽,然后這個虛擬表會和輸入行進行 join來達到連接 UDTF 外的 select 字段的目的

還是以上面的例子來看,Doris我怎么對這個寬表轉(zhuǎn)成高表从媚,實現(xiàn)就是借助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 '數(shù)學成績',
 `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    | ["數(shù)學", "92"]     |
| 001    | ["英語", "80"]     |
| 001    | ["政治", "0"]      |
| 002    | ["語文", "88"]     |
| 002    | ["數(shù)學", "90"]     |
| 002    | ["英語", "75.5"]   |
| 002    | ["政治", "0"]      |
| 003    | ["語文", "70"]     |
| 003    | ["數(shù)學", "85"]     |
| 003    | ["英語", "90"]     |
| 003    | ["政治", "82"]     |
+--------+--------------------+
12 rows in set (0.02 sec)
  1. 然后對這個上面的 arr 字符串,借助于 Doris 提供的 SPLIT_BY_STRING 函數(shù)完成字符串轉(zhuǎn)數(shù)組的動作

  2. 最后遍歷數(shù)組

  3. 完成列轉(zhuǎn)行的效果

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( '數(shù)學', ',', 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( '數(shù)學', ',', 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    | 數(shù)學    | 92    |
| 001    | 英語    | 80    |
| 001    | 政治    | 0     |
| 002    | 語文    | 88    |
| 002    | 數(shù)學    | 90    |
| 002    | 英語    | 75.5  |
| 002    | 政治    | 0     |
| 003    | 語文    | 70    |
| 003    | 數(shù)學    | 85    |
| 003    | 英語    | 90    |
| 003    | 政治    | 82    |
+--------+---------+-------+
12 rows in set (0.02 sec)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拜效,一起剝皮案震驚了整個濱河市喷众,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌紧憾,老刑警劉巖到千,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稻励,居然都是意外死亡父阻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門望抽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來加矛,“玉大人,你說我怎么就攤上這事煤篙≌謇溃” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵辑奈,是天一觀的道長苛茂。 經(jīng)常有香客問我,道長鸠窗,這世上最難降的妖魔是什么妓羊? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮稍计,結(jié)果婚禮上躁绸,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好净刮,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布剥哑。 她就那樣靜靜地躺著,像睡著了一般淹父。 火紅的嫁衣襯著肌膚如雪株婴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天暑认,我揣著相機與錄音困介,去河邊找鬼。 笑死穷吮,一個胖子當著我的面吹牛逻翁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捡鱼,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼八回,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了驾诈?” 一聲冷哼從身側(cè)響起缠诅,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乍迄,沒想到半個月后管引,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡闯两,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年褥伴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漾狼。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡重慢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逊躁,到底是詐尸還是另有隱情似踱,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布稽煤,位于F島的核電站核芽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酵熙。R本人自食惡果不足惜轧简,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匾二。 院中可真熱鬧吉懊,春花似錦庐橙、人聲如沸假勿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽转培。三九已至恶导,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浸须,已是汗流浹背惨寿。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留删窒,地道東北人裂垦。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓蒙袍,卻偏偏與公主長得像舱污,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刻两,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容