寫在前面: 博主是一名軟件工程系大數據應用開發專業大二的學生,暱稱來源於《愛麗絲夢遊仙境》中的Alice和自己的暱稱。作爲一名互聯網小白,
寫博客一方面是爲了記錄自己的學習歷程,一方面是希望能夠幫助到很多和自己一樣處於起步階段的萌新
。由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!個人小站:http://alices.ibilibili.xyz/ , 博客主頁:https://alice.blog.csdn.net/
儘管當前水平可能不及各位大佬,但我還是希望自己能夠做得更好,因爲一天的生活就是一生的縮影
。我希望在最美的年華,做最好的自己!
文章目錄
1.概述
相信用過MySQL的朋友都知道,MySQL中也有開窗函數的存在。開窗函數的引入是爲了既顯示聚集前的數據,又顯示聚集後的數據。即在每一行的最後一列添加聚合函數的結果。
開窗用於爲行定義一個窗口(這裏的窗口是指運算將要操作的行的集合),它對一組值進行操作,不需要使用 GROUP BY 子句對數據進行分組,能夠在同一行中同時返回基礎行的列和聚合列。
聚合函數是將多行變成一行,count,avg…
開窗函數是將一行變成多行;
聚合函數如果要顯示其他的列必須將列加入到group by中
開窗函數可以不使用group by,直接將所有信息顯示出來
- 聚合開窗函數
聚合函數(列) OVER(選項),這裏的選項可以是PARTITION BY 子句,但不可以是 ORDER BY 子句。
- 排序開窗函數
排序函數(列) OVER(選項),這裏的選項可以是ORDER BY 子句,也可以是OVER(PARTITION BY 子句 ORDER BY 子句),但不可以是 PARTITION BY 子句。
2. 準備工作
進入到SparkShell命令行
/export/servers/spark/bin/spark-shell --master spark://node01:7077,node02:7077
創建一個樣例類,用於封裝數據
case class Score(name: String, clazz: Int, score: Int)
創建一個RDD數組,造一些數據,並調用toDF
方法將其轉換成DataFrame
val scoreDF = spark.sparkContext.makeRDD(Array(
Score("a1", 1, 80),
Score("a2", 1, 78),
Score("a3", 1, 95),
Score("a4", 2, 74),
Score("a5", 2, 92),
Score("a6", 3, 99),
Score("a7", 3, 99),
Score("a8", 3, 45),
Score("a9", 3, 55),
Score("a10", 3, 78),
Score("a11", 3, 100))
).toDF("name", "class", "score")
創建一個臨時表
scoreDF.createOrReplaceTempView("scores")
查詢所有數據
scoreDF.show()
+----+-----+-----+
|name|class|score|
+----+-----+-----+
| a1| 1| 80|
| a2| 1| 78|
| a3| 1| 95|
| a4| 2| 74|
| a5| 2| 92|
| a6| 3| 99|
| a7| 3| 99|
| a8| 3| 45|
| a9| 3| 55|
| a10| 3| 78|
| a11| 3| 100|
+----+-----+-----+
3. 聚合開窗函數
- 示例1
OVER 關鍵字表示把聚合函數當成聚合開窗函數而不是聚合函數。
SQL標準允許將所有聚合函數用做聚合開窗函數。
spark.sql("select count(name) from scores").show
spark.sql("select name, class, score, count(name) over() name_count from scores").show
查詢結果如下所示:
查詢結果如下所示:
+----+-----+-----+----------+
|name|class|score|name_count|
+----+-----+-----+----------+
| a1| 1| 80| 11|
| a2| 1| 78| 11|
| a3| 1| 95| 11|
| a4| 2| 74| 11|
| a5| 2| 92| 11|
| a6| 3| 99| 11|
| a7| 3| 99| 11|
| a8| 3| 45| 11|
| a9| 3| 55| 11|
| a10| 3| 78| 11|
| a11| 3| 100| 11|
+----+-----+-----+----------+
- 示例2
OVER 關鍵字後的括號中還可以添加選項用以改變進行聚合運算的窗口範圍。
如果 OVER 關鍵字後的括號中的選項爲空,則開窗函數會對結果集中的所有行進行聚合運算。
開窗函數的 OVER 關鍵字後括號中的可以使用 PARTITION BY 子句來定義行的分區來供進行聚合計算。與 GROUP BY 子句不同,PARTITION BY 子句創建的分區是獨立於結果集的,創建的分區只是供進行聚合計算的,而且不同的開窗函數所創建的分區也不互相影響。
下面的 SQL 語句用於顯示按照班級分組後每組的人數:
OVER(PARTITION BY class)表示對結果集按照 class 進行分區,並且計算當前行所屬的組的聚合計算結果。
spark.sql("select name, class, score, count(name) over(partition by class) name_count from scores").show
查詢結果如下所示:
+----+-----+-----+----------+
|name|class|score|name_count|
+----+-----+-----+----------+
| a1| 1| 80| 3|
| a2| 1| 78| 3|
| a3| 1| 95| 3|
| a6| 3| 99| 6|
| a7| 3| 99| 6|
| a8| 3| 45| 6|
| a9| 3| 55| 6|
| a10| 3| 78| 6|
| a11| 3| 100| 6|
| a4| 2| 74| 2|
| a5| 2| 92| 2|
+----+-----+-----+----------+
4. 排序開窗函數
4.1 ROW_NUMBER順序排序
row_number() over(order by score) as rownum 表示按score 升序的方式來排序,並得出排序結果的序號
注意:
在排序開窗函數中使用 PARTITION BY 子句需要放置在ORDER BY 子句之前。
- 示例1
spark.sql("select name, class, score, row_number() over(order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a8| 3| 45| 1|
| a9| 3| 55| 2|
| a4| 2| 74| 3|
| a2| 1| 78| 4|
| a10| 3| 78| 5|
| a1| 1| 80| 6|
| a5| 2| 92| 7|
| a3| 1| 95| 8|
| a6| 3| 99| 9|
| a7| 3| 99| 10|
| a11| 3| 100| 11|
+----+-----+-----+----+
spark.sql("select name, class, score, row_number() over(partition by class order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a2| 1| 78| 1|
| a1| 1| 80| 2|
| a3| 1| 95| 3|
| a8| 3| 45| 1|
| a9| 3| 55| 2|
| a10| 3| 78| 3|
| a6| 3| 99| 4|
| a7| 3| 99| 5|
| a11| 3| 100| 6|
| a4| 2| 74| 1|
| a5| 2| 92| 2|
+----+-----+-----+----+
4.2 RANK跳躍排序
rank() over(order by score) as rank表示按 score升序的方式來排序,並得出排序結果的排名號。
這個函數求出來的排名結果可以並列(並列第一/並列第二),並列排名之後的排名將是並列的排名加上並列數
簡單說每個人只有一種排名,然後出現兩個並列第一名的情況,這時候排在兩個第一名後面的人將是第三名,也就是沒有了第二名,但是有兩個第一名。
- 實例2
spark.sql("select name, class, score, rank() over(order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a8| 3| 45| 1|
| a9| 3| 55| 2|
| a4| 2| 74| 3|
| a10| 3| 78| 4|
| a2| 1| 78| 4|
| a1| 1| 80| 6|
| a5| 2| 92| 7|
| a3| 1| 95| 8|
| a6| 3| 99| 9|
| a7| 3| 99| 9|
| a11| 3| 100| 11|
+----+-----+-----+----+
spark.sql("select name, class, score, rank() over(partition by class order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a2| 1| 78| 1|
| a1| 1| 80| 2|
| a3| 1| 95| 3|
| a8| 3| 45| 1|
| a9| 3| 55| 2|
| a10| 3| 78| 3|
| a6| 3| 99| 4|
| a7| 3| 99| 4|
| a11| 3| 100| 6|
| a4| 2| 74| 1|
| a5| 2| 92| 2|
+----+-----+-----+----+
4.3 DENSE_RANK連續排序
dense_rank() over(order by score) as dense_rank 表示按score 升序的方式來排序,並得出排序結果的排名號。
這個函數並列排名之後的排名是並列排名加1
簡單說每個人只有一種排名,然後出現兩個並列第一名的情況,這時候排在兩個第一名後面的人將是第二名,也就是兩個第一名,一個第二名
- 實例3
spark.sql("select name, class, score, dense_rank() over(order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a8| 3| 45| 1|
| a9| 3| 55| 2|
| a4| 2| 74| 3|
| a2| 1| 78| 4|
| a10| 3| 78| 4|
| a1| 1| 80| 5|
| a5| 2| 92| 6|
| a3| 1| 95| 7|
| a6| 3| 99| 8|
| a7| 3| 99| 8|
| a11| 3| 100| 9|
+----+-----+-----+----+
spark.sql("select name, class, score, dense_rank() over(partition by class order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a2| 1| 78| 1|
| a1| 1| 80| 2|
| a3| 1| 95| 3|
| a8| 3| 45| 1|
| a9| 3| 55| 2|
| a10| 3| 78| 3|
| a6| 3| 99| 4|
| a7| 3| 99| 4|
| a11| 3| 100| 5|
| a4| 2| 74| 1|
| a5| 2| 92| 2|
+----+-----+-----+----+
4.4 NTILE分組排名[瞭解]
ntile(6) over(order by score)as ntile表示按 score 升序的方式來排序,然後 6 等分成 6 個組,並顯示所在組的序號。
- 實例4
spark.sql("select name, class, score, ntile(6) over(order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a8| 3| 45| 1|
| a9| 3| 55| 1|
| a4| 2| 74| 2|
| a2| 1| 78| 2|
| a10| 3| 78| 3|
| a1| 1| 80| 3|
| a5| 2| 92| 4|
| a3| 1| 95| 4|
| a6| 3| 99| 5|
| a7| 3| 99| 5|
| a11| 3| 100| 6|
+----+-----+-----+----+
spark.sql("select name, class, score, ntile(6) over(partition by class order by score) rank from scores").show()
+----+-----+-----+----+
|name|class|score|rank|
+----+-----+-----+----+
| a2| 1| 78| 1|
| a1| 1| 80| 2|
| a3| 1| 95| 3|
| a8| 3| 45| 1|
| a9| 3| 55| 2|
| a10| 3| 78| 3|
| a6| 3| 99| 4|
| a7| 3| 99| 5|
| a11| 3| 100| 6|
| a4| 2| 74| 1|
| a5| 2| 92| 2|
+----+-----+-----+----+
結語
本次的分享就到這裏,受益的朋友或對大數據技術感興趣的夥伴可以點個贊關注一下博主,後續會持續更新大數據的相關內容,敬請期待(✪ω✪)