0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度剖析SQL中的Grouping Sets語句1

jf_78858299 ? 來源:元閏子的邀請 ? 作者:元閏子 ? 2023-05-10 17:44 ? 次閱讀

前言

SQL 中 Group By 語句大家都很熟悉, 根據(jù)指定的規(guī)則對數(shù)據(jù)進(jìn)行分組 ,常常和聚合函數(shù)一起使用。

比如,考慮有表 dealer,表中數(shù)據(jù)如下:

id (Int) city (String) car_model (String) quantity (Int)
100 Fremont Honda Civic 10
100 Fremont Honda Accord 15
100 Fremont Honda CRV 7
200 Dublin Honda Civic 20
200 Dublin Honda Accord 10
200 Dublin Honda CRV 3
300 San Jose Honda Civic 5
300 San Jose Honda Accord 8

如果執(zhí)行 SQL 語句 SELECT id, sum(quantity) FROM dealer GROUP BY id ORDER BY id,會得到如下結(jié)果:

+---+-------------+
| id|sum(quantity)|
+---+-------------+
|100|           32|
|200|           33|
|300|           13|
+---+-------------+

上述 SQL 語句的意思就是對數(shù)據(jù)按 id 列進(jìn)行分組,然后在每個(gè)分組內(nèi)對 quantity 列進(jìn)行求和。

Group By 語句除了上面的簡單用法之外,還有更高級的用法,常見的是 Grouping Sets、RollUpCube,它們在 OLAP 時(shí)比較常用。其中,RollUpCube 都是以 Grouping Sets 為基礎(chǔ)實(shí)現(xiàn)的,因此,弄懂了 Grouping Sets,也就理解了 RollUpCube 。

本文首先簡單介紹 Grouping Sets 的用法,然后以 Spark SQL 作為切入點(diǎn),深入解析 Grouping Sets 的實(shí)現(xiàn)機(jī)制。

Spark SQL 是 Apache Spark 大數(shù)據(jù)處理框架的一個(gè)子模塊,用來處理結(jié)構(gòu)化信息。它可以將 SQL 語句翻譯多個(gè)任務(wù)在 Spark 集群上執(zhí)行, 允許用戶直接通過 SQL 來處理數(shù)據(jù) ,大大提升了易用性。

Grouping Sets 簡介

Spark SQL 官方文檔中 SQL Syntax 一節(jié)對 Grouping Sets 語句的描述如下:

Groups the rows for each grouping set specified after GROUPING SETS. (... 一些舉例) This clause is a shorthand for a UNION ALLwhere each leg of the UNION ALL operator performs aggregation of each grouping set specified in the GROUPING SETS clause. (... 一些舉例)

也即,Grouping Sets 語句的作用是指定幾個(gè) grouping set 作為 Group By 的分組規(guī)則,然后再將結(jié)果聯(lián)合在一起。它的效果和, 先分別對這些 grouping set 進(jìn)行 Group By 分組之后,再通過 Union All 將結(jié)果聯(lián)合起來 ,是一樣的。

比如,對于 dealer 表,Group By Grouping Sets ((city, car_model), (city), (car_model), ())Union All((Group By city, car_model), (Group By city), (Group By car_model), 全局聚合) 的效果是相同的:

先看 Grouping Sets 版的執(zhí)行結(jié)果:

spark-sql> SELECT city, car_model, sum(quantity) AS sum FROM dealer 
         > GROUP BY GROUPING SETS ((city, car_model), (city), (car_model), ()) 
         > ORDER BY city, car_model;
+--------+------------+---+
|    city|   car_model|sum|
+--------+------------+---+
|    null|        null| 78|
|    null|Honda Accord| 33|
|    null|   Honda CRV| 10|
|    null| Honda Civic| 35|
|  Dublin|        null| 33|
|  Dublin|Honda Accord| 10|
|  Dublin|   Honda CRV|  3|
|  Dublin| Honda Civic| 20|
| Fremont|        null| 32|
| Fremont|Honda Accord| 15|
| Fremont|   Honda CRV|  7|
| Fremont| Honda Civic| 10|
|San Jose|        null| 13|
|San Jose|Honda Accord|  8|
|San Jose| Honda Civic|  5|
+--------+------------+---+

再看 Union All 版的執(zhí)行結(jié)果:

spark-sql> (SELECT city, car_model, sum(quantity) AS sum FROM dealer GROUP BY city, car_model) UNION ALL 
         > (SELECT city, NULL as car_model, sum(quantity) AS sum FROM dealer GROUP BY city) UNION ALL 
         > (SELECT NULL as city, car_model, sum(quantity) AS sum FROM dealer GROUP BY car_model) UNION ALL 
         > (SELECT NULL as city, NULL as car_model, sum(quantity) AS sum FROM dealer) 
         > ORDER BY city, car_model;
+--------+------------+---+
|    city|   car_model|sum|
+--------+------------+---+
|    null|        null| 78|
|    null|Honda Accord| 33|
|    null|   Honda CRV| 10|
|    null| Honda Civic| 35|
|  Dublin|        null| 33|
|  Dublin|Honda Accord| 10|
|  Dublin|   Honda CRV|  3|
|  Dublin| Honda Civic| 20|
| Fremont|        null| 32|
| Fremont|Honda Accord| 15|
| Fremont|   Honda CRV|  7|
| Fremont| Honda Civic| 10|
|San Jose|        null| 13|
|San Jose|Honda Accord|  8|
|San Jose| Honda Civic|  5|
+--------+------------+---+

兩版的查詢結(jié)果完全一樣。

Grouping Sets 的執(zhí)行計(jì)劃

從執(zhí)行結(jié)果上看,Grouping Sets 版本和 Union All 版本的 SQL 是等價(jià)的,但 Grouping Sets 版本更加簡潔。

那么,Grouping Sets 僅僅只是 Union All 的一個(gè)縮寫,或者語法糖嗎 ?

為了進(jìn)一步探究 Grouping Sets 的底層實(shí)現(xiàn)是否和 Union All 是一致的,我們可以來看下兩者的執(zhí)行計(jì)劃。

首先,我們通過 explain extended 來查看 Union All 版本的 Optimized Logical Plan :

spark-sql> explain extended (SELECT city, car_model, sum(quantity) AS sum FROM dealer GROUP BY city, car_model) UNION ALL (SELECT city, NULL as car_model, sum(quantity) AS sum FROM dealer GROUP BY city) UNION ALL (SELECT NULL as city, car_model, sum(quantity) AS sum FROM dealer GROUP BY car_model) UNION ALL (SELECT NULL as city, NULL as car_model, sum(quantity) AS sum FROM dealer) ORDER BY city, car_model;
== Parsed Logical Plan ==
...
== Analyzed Logical Plan ==
...
== Optimized Logical Plan ==
Sort [city#93 ASC NULLS FIRST, car_model#94 ASC NULLS FIRST], true
+- Union false, false
   :- Aggregate [city#93, car_model#94], [city#93, car_model#94, sum(quantity#95) AS sum#79L]
   :  +- Project [city#93, car_model#94, quantity#95]
   :     +- HiveTableRelation [`default`.`dealer`, ..., Data Cols: [id#92, city#93, car_model#94, quantity#95], Partition Cols: []]
   :- Aggregate [city#97], [city#97, null AS car_model#112, sum(quantity#99) AS sum#81L]
   :  +- Project [city#97, quantity#99]
   :     +- HiveTableRelation [`default`.`dealer`, ..., Data Cols: [id#96, city#97, car_model#98, quantity#99], Partition Cols: []]
   :- Aggregate [car_model#102], [null AS city#113, car_model#102, sum(quantity#103) AS sum#83L]
   :  +- Project [car_model#102, quantity#103]
   :     +- HiveTableRelation [`default`.`dealer`, ..., Data Cols: [id#100, city#101, car_model#102, quantity#103], Partition Cols: []]
   +- Aggregate [null AS city#114, null AS car_model#115, sum(quantity#107) AS sum#86L]
      +- Project [quantity#107]
         +- HiveTableRelation [`default`.`dealer`, ..., Data Cols: [id#104, city#105, car_model#106, quantity#107], Partition Cols: []]
== Physical Plan ==
...

從上述的 Optimized Logical Plan 可以清晰地看出 Union All 版本的執(zhí)行邏輯:

  1. 執(zhí)行每個(gè)子查詢語句,計(jì)算得出查詢結(jié)果。其中,每個(gè)查詢語句的邏輯是這樣的:
    • HiveTableRelation 節(jié)點(diǎn)對 dealer 表進(jìn)行全表掃描。
    • Project 節(jié)點(diǎn)選出與查詢語句結(jié)果相關(guān)的列,比如對于子查詢語句 SELECT NULL as city, NULL as car_model, sum(quantity) AS sum FROM dealer,只需保留 quantity 列即可。
    • Aggregate 節(jié)點(diǎn)完成 quantity 列對聚合運(yùn)算。在上述的 Plan 中,Aggregate 后面緊跟的就是用來分組的列,比如 Aggregate [city#902] 就表示根據(jù) city 列來進(jìn)行分組。
  2. Union 節(jié)點(diǎn)完成對每個(gè)子查詢結(jié)果的聯(lián)合。
  3. 最后,在 Sort 節(jié)點(diǎn)完成對數(shù)據(jù)的排序,上述 Plan 中 Sort [city#93 ASC NULLS FIRST, car_model#94 ASC NULLS FIRST] 就表示根據(jù) citycar_model 列進(jìn)行升序排序。

圖片

接下來,我們通過 explain extended 來查看 Grouping Sets 版本的 Optimized Logical Plan:

spark-sql> explain extended SELECT city, car_model, sum(quantity) AS sum FROM dealer GROUP BY GROUPING SETS ((city, car_model), (city), (car_model), ()) ORDER BY city, car_model;
== Parsed Logical Plan ==
...
== Analyzed Logical Plan ==
...
== Optimized Logical Plan ==
Sort [city#138 ASC NULLS FIRST, car_model#139 ASC NULLS FIRST], true
+- Aggregate [city#138, car_model#139, spark_grouping_id#137L], [city#138, car_model#139, sum(quantity#133) AS sum#124L]
   +- Expand [[quantity#133, city#131, car_model#132, 0], [quantity#133, city#131, null, 1], [quantity#133, null, car_model#132, 2], [quantity#133, null, null, 3]], [quantity#133, city#138, car_model#139, spark_grouping_id#137L]
      +- Project [quantity#133, city#131, car_model#132]
         +- HiveTableRelation [`default`.`dealer`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [id#130, city#131, car_model#132, quantity#133], Partition Cols: []]
== Physical Plan ==
...

從 Optimized Logical Plan 來看,Grouping Sets 版本要簡潔很多!具體的執(zhí)行邏輯是這樣的:

  1. HiveTableRelation 節(jié)點(diǎn)對 dealer 表進(jìn)行全表掃描。
  2. Project 節(jié)點(diǎn)選出與查詢語句結(jié)果相關(guān)的列。
  3. 接下來的 Expand 節(jié)點(diǎn)是關(guān)鍵,數(shù)據(jù)經(jīng)過該節(jié)點(diǎn)后,多出了 spark_grouping_id 列。從 Plan 中可以看出來,Expand 節(jié)點(diǎn)包含了 Grouping Sets 里的各個(gè) grouping set 信息,比如 [quantity#133, city#131, null, 1] 對應(yīng)的就是 (city) 這一 grouping set。而且,每個(gè) grouping set 對應(yīng)的 spark_grouping_id 列的值都是固定的,比如 (city) 對應(yīng)的 spark_grouping_id1。
  4. Aggregate 節(jié)點(diǎn)完成 quantity 列對聚合運(yùn)算,其中分組的規(guī)則為 city, car_model, spark_grouping_id。注意,數(shù)據(jù)經(jīng)過 Aggregate 節(jié)點(diǎn)后,spark_grouping_id 列被刪除了!
  5. 最后,在 Sort 節(jié)點(diǎn)完成對數(shù)據(jù)的排序。

圖片

從 Optimized Logical Plan 來看,雖然 Union All 版本和 Grouping Sets 版本的效果一致,但它們的底層實(shí)現(xiàn)有著巨大的差別。

其中,Grouping Sets 版本的 Plan 中最關(guān)鍵的是 Expand 節(jié)點(diǎn),目前,我們只知道數(shù)據(jù)經(jīng)過它之后,多出了 spark_grouping_id 列。而且從最終結(jié)果來看,spark_grouping_id只是 Spark SQL 的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),對用戶并不體現(xiàn)。那么:

  1. Expand 的實(shí)現(xiàn)邏輯是怎樣的,為什么能達(dá)到 Union All 的效果?
  2. Expand 節(jié)點(diǎn)的輸出數(shù)據(jù)是怎樣的 ?
  3. spark_grouping_id 列的作用是什么 ?

通過 Physical Plan,我們發(fā)現(xiàn) Expand 節(jié)點(diǎn)對應(yīng)的算子名稱也是 Expand:

== Physical Plan ==
AdaptiveSparkPlan isFinalPlan=false
+- Sort [city#138 ASC NULLS FIRST, car_model#139 ASC NULLS FIRST], true, 0
   +- Exchange rangepartitioning(city#138 ASC NULLS FIRST, car_model#139 ASC NULLS FIRST, 200), ENSURE_REQUIREMENTS, [plan_id=422]
      +- HashAggregate(keys=[city#138, car_model#139, spark_grouping_id#137L], functions=[sum(quantity#133)], output=[city#138, car_model#139, sum#124L])
         +- Exchange hashpartitioning(city#138, car_model#139, spark_grouping_id#137L, 200), ENSURE_REQUIREMENTS, [plan_id=419]
            +- HashAggregate(keys=[city#138, car_model#139, spark_grouping_id#137L], functions=[partial_sum(quantity#133)], output=[city#138, car_model#139, spark_grouping_id#137L, sum#141L])
               +- Expand [[quantity#133, city#131, car_model#132, 0], [quantity#133, city#131, null, 1], [quantity#133, null, car_model#132, 2], [quantity#133, null, null, 3]], [quantity#133, city#138, car_model#139, spark_grouping_id#137L]
                  +- Scan hive default.dealer [quantity#133, city#131, car_model#132], HiveTableRelation [`default`.`dealer`, ..., Data Cols: [id#130, city#131, car_model#132, quantity#133], Partition Cols: []]

帶著前面的幾個(gè)問題,接下來我們深入 Spark SQL 的 Expand 算子源碼尋找答案。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    6715

    瀏覽量

    88316
  • SQL
    SQL
    +關(guān)注

    關(guān)注

    1

    文章

    750

    瀏覽量

    43900
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4237

    瀏覽量

    61973
收藏 人收藏

    評論

    相關(guān)推薦

    在Delphi動態(tài)地使用SQL查詢語句

    程序的查詢語句。假定程序的窗體中有一個(gè)名為Query1的TQuery構(gòu)件,在程序運(yùn)行過程需要改變它的SQL查詢
    發(fā)表于 05-10 11:10

    SQL語句的兩種嵌套方式

    一般情況下,SQL語句是嵌套在宿主語言(如C語言)的。有兩種嵌套方式:1.調(diào)用層接口(CLI):提供一些庫,庫的函數(shù)和方法實(shí)現(xiàn)
    發(fā)表于 05-23 08:51

    區(qū)分SQL語句與主語言語句

    為了區(qū)分SQL語句與主語言語句,所有SQL 語句必須加前綴EXEC SQL處理過程:含嵌入式
    發(fā)表于 10-28 08:44

    為什么要?jiǎng)討B(tài)sql語句

    為什么要?jiǎng)討B(tài)sql語句?因?yàn)閯討B(tài)sql語句能夠提供一些比較友好的機(jī)制1、可以使得一些在編譯過程
    發(fā)表于 12-20 06:00

    數(shù)據(jù)庫SQL語句電子教程

    電子發(fā)燒友為您提供了數(shù)據(jù)庫SQL語句電子教程,幫助您了解數(shù)據(jù)庫 SQL語句 ,學(xué)習(xí)讀懂?dāng)?shù)據(jù)庫SQL語句
    發(fā)表于 07-14 17:09 ?0次下載

    sql語句實(shí)例講解

    SQL是用來存取關(guān)系數(shù)據(jù)庫的語言,具有查詢、操縱、定義和控制關(guān)系型數(shù)據(jù)庫的四方面功能。常見的關(guān)系數(shù)據(jù)庫有Oracle,SQLServer,DB2,Sybase。開源不收費(fèi)的有MYSQL,SQLLite等。今天我們主要以MYSQL為例子,講解SQL常用的
    發(fā)表于 11-17 12:39 ?8997次閱讀
    <b class='flag-5'>sql</b><b class='flag-5'>語句</b>實(shí)例講解

    如何使用navicat或PHPMySQLAdmin導(dǎo)入SQL語句

    很多朋友問我們怎么導(dǎo)入SQL語句,這是新人最需要知道的東西,現(xiàn)制作圖文教程,希望對新手有所幫助,順便文末附SQL語句導(dǎo)入導(dǎo)出大全,高手可以提供更加詳細(xì)的教程。
    發(fā)表于 04-10 15:06 ?2次下載

    VSSQL命令語句的詳細(xì)資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是微軟VS(Microsoft Visual Studio)SQL命令語句的詳細(xì)資料免費(fèi)下載
    發(fā)表于 10-09 11:45 ?8次下載

    如何使用SQL修復(fù)語句程序說明

    本文檔的主要內(nèi)容詳細(xì)介紹的是如何使用SQL修復(fù)語句程序說明。
    發(fā)表于 10-31 15:09 ?5次下載

    嵌入式SQL語句

    為了區(qū)分SQL語句與主語言語句,所有SQL 語句必須加前綴EXEC SQL處理過程:含嵌入式
    發(fā)表于 10-21 11:51 ?4次下載
    嵌入式<b class='flag-5'>SQL</b><b class='flag-5'>語句</b>

    Group By高級用法Groupings Sets語句的功能和底層實(shí)現(xiàn)

    SQL Group By 語句大家都很熟悉,根據(jù)指定的規(guī)則對數(shù)據(jù)進(jìn)行分組,常常和聚合函數(shù)一起使用。
    的頭像 發(fā)表于 07-04 10:26 ?2982次閱讀

    Java如何解析、格式化、生成SQL語句

    昨天在群里看到有小伙伴問,Java里如何解析SQL語句然后格式化SQL,是否有現(xiàn)成類庫可以使用?
    的頭像 發(fā)表于 04-10 11:59 ?853次閱讀

    深度剖析SQLGrouping Sets語句2

    SQL `Group By` 語句大家都很熟悉, **根據(jù)指定的規(guī)則對數(shù)據(jù)進(jìn)行分組** ,常常和**聚合函數(shù)**一起使用。
    的頭像 發(fā)表于 05-10 17:44 ?501次閱讀
    <b class='flag-5'>深度</b><b class='flag-5'>剖析</b><b class='flag-5'>SQL</b><b class='flag-5'>中</b>的<b class='flag-5'>Grouping</b> <b class='flag-5'>Sets</b><b class='flag-5'>語句</b>2

    sql查詢語句大全及實(shí)例

    SQL(Structured Query Language)是一種專門用于數(shù)據(jù)庫管理系統(tǒng)的標(biāo)準(zhǔn)交互式數(shù)據(jù)庫查詢語言。它被廣泛應(yīng)用于數(shù)據(jù)庫管理和數(shù)據(jù)操作領(lǐng)域。在本文中,我們將為您詳細(xì)介紹SQL查詢語句
    的頭像 發(fā)表于 11-17 15:06 ?1161次閱讀

    oracle執(zhí)行sql查詢語句的步驟是什么

    Oracle數(shù)據(jù)庫是一種常用的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),具有強(qiáng)大的SQL查詢功能。Oracle執(zhí)行SQL查詢語句的步驟包括編寫SQL語句、解析
    的頭像 發(fā)表于 12-06 10:49 ?775次閱讀