【学习笔记】分析函数(开窗函数)
分析函數
這里的分析函數也就是我們常說的開窗函數,通常有兩類:
一類是聚合開窗函數(SUM、AVG、MAX、MIN、COUNT等),
一類是排序開窗函數(ROW_NUMBER、DENSE_RANK、RANK等)。
本文主要內容轉自《高效使用Greenplum》一書。
分析函數是Greenplum數據庫管理系統自帶函數中的一種專門解決具有復雜統計需求的函數,它可以對數據分組,然后基于組中數據進行分析統計,最后在每組數據集的每一行返回這個統計值。
分析函數不同于分組統計(Group By),分組統計只能按照分組字段返回一個固定的統計值,不能在原來的數據行上附帶這個統計值,而分析函數正式專門解決這類統計需求所開發出來的函數。分析函數已經逐步稱為SQL標準的一部分,有越來越多的數據庫系統開始支持分析函數。
開窗函數和聚合函數的區別如下:
- SQL 標準允許將所有聚合函數用作開窗函數,用OVER 關鍵字區分開窗函數和聚合函數。
- 聚合函數每組只返回一個值,開窗函數每組可返回多個值。
分析函數的語法結構如下:
SELECT table.column,
analysis_function() OVER ( [PARTITION BY 字符] [ORDER BY 字段 [rows]] ) as 統計值
FROM table
其中:
analysis_function():指定分析函數名稱,常用的分析函數有sum、max、first_value、last_value、lag、lead、rank、desn_rank、row_number等。
OVER():開窗函數名,PARTITION BY指定進行數據分組的字段,ORDER BY指定進行排序的字段,ROWS指定數據窗口(即指定分析函數要操作的行數),語法形式為OVER(PARTITION BY xxx ORDER BY yyy ROWS BETWEEN zzz)。
這里的ROWS有多個范圍值(一般情況下會省略),具體如下:
案例:下圖是員工表和部門表數據內連接后的結果集。
select b.dept_id,b.dept_name,t.emp_id,t.emp_name,t.age,t.salary
from emp t,dept b
where t.dept_id = b.dept_id
| dept_id | dept_name | emp_id | emp_name | age | salary |
| 1100 | 銷售部 | 3 | Jack | 40 | 15000 |
| 1100 | 銷售部 | 4 | Michael | 36 | 9800 |
| 1200 | 研發部 | 2 | Alen | 29 | 13500 |
| 1200 | 研發部 | 5 | Lily | 36 | 12500 |
| 1110 | 銷售一部 | 6 | David | 30 | 7900 |
| 1120 | 銷售二部 | 7 | Timmy | 26 | 8500 |
| 1000 | 總裁辦 | 1 | Paul | 45 | 36000 |
案例1:利用min(),max()分析函數分別取出不同部門不同員工工資的最高值和最低值,附帶在原始數據上。
select b.dept_id,b.dept_name,t.emp_id,t.emp_name,t.age,t.salary
-- 獲取組中工資最高值
max(t.salary) OVER(PARTITION BY t.dept_id) AS salary_max,
-- 獲取組中工資最低值
min(t.salary) OVER(PARTITION BY t.dept_id) AS salary_min,
-- 分組窗口的第一個值(指定窗口為組中第一行到末尾行)
first_value(t.salary) OVER(PARTITION BY t.dept_id ORDER BY t.salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS salary_first,
-- 分組窗口的最后一個值(指定窗口為組中第一行到末尾行)
last_value(t.salary) OVER(PARTITION BY t.dept_id ORDER BY t.salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS salary_last,
-- 分組窗口的第一個值(不指定窗口)
first_value(t.salary) OVER(PARTITION BY t.dept_id ORDER BY t.salary DESC) AS salary_first_1,
-- 分組窗口的最后一個值(指定窗口才可以取到最低值,否則只能取到當前行)
last_value(t.salary) OVER(PARTITION BY t.dept_id ORDER BY t.salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS salary_last_1
from emp t,dept b
where t.dept_id = b.dept_id
order by t.dept_id,t.emp_id
根據查詢結果可以看出如下信息:
min()和max()分析函數直接獲取組中最小值和最大值;
first_value()和last_value()返回窗口的第一行和最后一行數據,因為我們通過工資字段對分組內的數據進行了降序排列,所以也可以達到在一定的窗口內獲取最大值和最小值的功能;
排序不指定窗口時,就按照組內的第一行到當前行作為窗口,然后取出窗口的第一行和最后一行;
窗口子語句當中的第一行是UNBOUNDED PRECEDING,當前行是CURRENT ROW,最后一行是UNBOUNDED FOLLOWING,正是利用窗口范圍是第一行到最后一行,得到同一部門內的最高工資和最低工資。
注意:ROWS的默認值是:BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。
案例2:利用rank()、dense_rank()、row_number()函數對員工的年齡進行排序,比較三個不同關鍵字的差異。
select t.emp_id,t.emp.name,t.age,
row_number() OVER(ORDER BY t.age) AS “row_number排名”,
rank() OVER(ORDER BY t.age) AS “rank排名”,
dense_rank() OVER(ORDER BY t.age) AS “dense_rank排名”
from emp t
order by t.age
根據查詢結果可以看出如下信息:
| emp_id | emp_name | age | row_number | rank | dense_rank |
| 7 | Timmy | 26 | 1 | 1 | 1 |
| 2 | Alen | 29 | 2 | 2 | 2 |
| 6 | David | 30 | 3 | 3 | 3 |
| 4 | Michael | 36 | 4 | 4 | 4 |
| 5 | Lily | 36 | 5 | 4 | 4 |
| 3 | Jack | 40 | 6 | 6 | 5 |
| 1 | Paul | 45 | 7 | 7 | 6 |
row_number()函數排名返回唯一值,當遇到相同的數據時,排名按照記錄集中的記錄順序依次遞增;
rank()函數返回唯一值,當遇到相同的數據時,所有相同的數據的排名是一樣的,同時會在最后一條相同記錄和下一條不同記錄的排名之間空出排名;
dense_rank()函數返回唯一值,當遇到相同的數據時,所有相同數據的排名是一樣的,同時在最后一條相同記錄和下一條不同記錄的排名之間不空出排名。
案例3:利用開窗函數對員工工資進行不同條件的匯總,以便對比ORDER BY和PARTITION BY的作用。
select t.emp_id,t.emp_name,t.age,t.salary,
sum(t.salary) OVER() AS “全局匯總”,
sum(t.salary) OVER(ORDER BY t.emp_id) AS “逐行累加”,
sum(t.salary) OVER(PARTITION BY t.dept_id) AS “分組匯總”,
sum(t.salary) OVER(PARTITION BY t.dept_id ORDER BY t.emp_id) AS “分組逐行匯總”
from emp t
order by t.emp_id
根據查詢結果可以看出如下信息:
OVER()默認是全局匯總,即所有可以查到的行數指標合集,可用于計算占比;
OVER+ORDER BY用于根據條件逐行相加匯總,可用于計算類似于“工資占前80%的員工明細”之類的需求;
OVER+PARTITION BY用于分組匯總,可以計算分組的合計、分組的占比、分組的最大值和最小值等;
OVER+PARTITION BY+ORDER BY用于分組逐行匯總,可用于計算分組的排名,可以滿足例如“取每一個組的前五名”之類的需求;
案例4:查詢部門總工資大于所有部門平均總工資的部門員工信息及部門平均工資、公司平均工資。
select * from (
select emp_id,emp_name,dept_id,salary,
avg(salary) OVER(PARTITION BY dept_id) as dept_avg_salary,
avg(salary) OVER() as comp_avg_salary
from emp) t
where t.dept_avg_salary > t.comp_avg_salary;
總結
以上是生活随笔為你收集整理的【学习笔记】分析函数(开窗函数)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【学习笔记】MySQL 数据备份与恢复
- 下一篇: 【一周入门MySQL—1】数据库概述、数