SQL:SQL高階處理
阿新 • • 發佈:2020-10-12
視窗函式
什麼是視窗函式
- 視窗函式也稱為 OLAP函式
- OLAP 是 OnLine Analytical Processing 的簡稱,意思是對資料庫資料 進行實時分析處理
- 如市場分析、建立財務報表、建立計劃等日常性商務工作
視窗函式的語法
-
<視窗函式> OVER ([PARTITION BY <列清單>] ORDER BY <排序用列清單>)
-
能夠作為視窗函式使用的函式
- ①能夠作為視窗函式的聚合函式(SUM、AVG、COUNT、MAX、MIN)
- ②RANK、DENSE_RANK、ROW_NUMBER 等專用視窗函式
語法的基本使用方法——使用RANK函式
-
RANK 是用來計算記錄排序的函式
-
PARTITION BY 能夠設定排序的物件範圍
- 在橫向上對錶進行分組
-
ORDER BY 能夠指定按照哪一列、何種順序進行排序
- 決定縱向排序的規則
-
視窗函式兼具分組和排序兩種功能
- 通過 PARTITION BY 分組後的記錄集合稱為視窗,是代表範圍
-- 根據不同的商品種類,按照銷售單價從低到高的順序建立排序表 SELECT product_name, product_type, sale_price, RANK () OVER (PARTITION BY product_typeORDER BY sale_price) AS ranking FROM Product;
無需指定PARTITION BY
- 和使用沒有 GROUP BY 的聚合函式時的效果一樣,從上到下沒有分組的全部排序
-- 不指定PARTITION BY SELECT product_name, product_type, sale_price, RANK () OVER (ORDER BY sale_price) AS ranking FROM Product;
專用視窗函式的種類
-
RANK函式
- 計算排序時,如果存在相同位次的記錄,則會跳過之後的位次
- 例:有 3 條記錄排在第 1 位時:1 位、1 位、1 位、4 位……
-
DENSE_RANK函式
- 同樣是計算排序,即使存在相同位次的記錄,也不會跳過之後的位次
- 例:有 3 條記錄排在第 1 位時:1 位、1 位、1 位、2 位……
-
ROW_NUMBER函式
- 賦予唯一的連續位次
- 例:有 3 條記錄排在第 1 位時:1 位、2 位、3 位、4 位……
-
由於專用視窗函式無需引數,因此通常括號中都是空的
-- 比較RANK、DENSE_RANK、ROW_NUMBER的結果 SELECT product_name, product_type, sale_price, RANK () OVER (ORDER BY sale_price) AS ranking, DENSE_RANK () OVER (ORDER BY sale_price) AS dense_ranking, ROW_NUMBER () OVER (ORDER BY sale_price) AS row_num FROM Product;
視窗函式的適用範圍
- 原則上視窗函式只能在SELECT子句中使用
- 在SELECT 子句之外“使用視窗函式是沒有意義的”
作為視窗函式使用的聚合函式
- 所有的聚合函式都能用作視窗函式,其語法和專用視窗函式完全相同
-- 將SUM函式作為視窗函式使用 SELECT product_id, product_name, sale_price, SUM (sale_price) OVER (ORDER BY product_id) AS current_sum FROM Product; -- 將AVG函式作為視窗函式使用 SELECT product_id, product_name, sale_price, AVG (sale_price) OVER (ORDER BY product_id) AS current_avg FROM Product;
計算移動平均
-
視窗函式就是將表以視窗為單位進行分割,並在其中進行排序的函式。
-
在視窗中指定更加詳細的彙總範圍的備選功能,該備選功 能中的彙總範圍稱為框架
-
指定框架(彙總範圍)
-
ROWS(“行”)和 PRECEDING(“之前”)
-
“ ROWS 2 PRECEDING”,
- 就是將框架指定為“截止到之前 2 行”,也就是將作為彙總物件的記錄限 定為如下的“最靠近的 3行”
- 1.自身(當前記錄) 2.之前1行的記錄 3.之前2行的記錄
-
由於框架是根據當前記錄來確定的,因此和固定的視窗不同,其範圍會隨著當前記錄的變化而變化
- 這樣的統計方法稱為移動平均(moving average)
- 由於這種方法在希 望實時把握“最近狀態”時非常方便,因此常常會應用在對股市趨勢的實時跟蹤當中
-
使用關鍵字FOLLOWING(“之後”)替換 PRECEDING,就可以指 定“截止到之後 ~ 行”作為框架了
-
-
將當前記錄的前後行作為彙總物件
- 同時使用 PRECEDING(“之前”)和 FOLLOWING(“之後”)關 鍵字來實現
-- 指定“最靠近的3行”作為彙總物件 SELECT product_id, product_name, sale_price, AVG (sale_price) OVER (ORDER BY product_id ROWS 2 PRECEDING) AS moving_avg FROM Product; -- 將當前記錄的前後行作為彙總物件 SELECT product_id, product_name, sale_price, AVG (sale_price) OVER (ORDER BY product_id ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS moving_avg FROM Product;
兩個ORDER BY
- 使用視窗函式時必須要在 OVER 子句中使用 ORDER BY
- OVER 子句中的 ORDER BY 只是用來決定 視窗函式按照什麼樣的順序進行計算的,對結果的排列順序並沒有影響
- 在SELECT 語句的最後,使用ORDER BY 子句進行指定。這樣就能保證SELECT 語句的結果中記錄的排列順序了
- 將聚合函式作為視窗函式使用時,會以當前記錄為基準來決定彙總物件的記錄。
-- 無法保證如下SELECT語句的結果的排列順序 SELECT product_name, product_type, sale_price, RANK () OVER (ORDER BY sale_price) AS ranking FROM Product; -- 在語句末尾使用ORDER BY子句對結果進行排序 SELECT product_name, product_type, sale_price, RANK () OVER (ORDER BY sale_price) AS ranking FROM Product ORDER BY ranking;
GROUPING運算子
同時得到合計行
- UNION ALL
-- 使用GROUP BY無法得到合計行 SELECT product_type, SUM(sale_price) FROM Product GROUP BY product_type; -- 分別計算出合計行和彙總結果再通過UNION ALL進行連線 SELECT '合計' AS product_type, SUM(sale_price) FROM Product UNION ALL SELECT product_type, SUM(sale_price) FROM Product GROUP BY product_type;
GROUPING 運算子包含以下 3種:ROLLUP,CUBE,GROUPING SETS
ROLLUP
-
同時得出合計和小計
-
使用方法
- 從語法上來說,就是將 GROUP BY 子句中的聚合鍵清單像 ROLLUP (<列1>,< 列2>,...)這樣使用
- 該運算子的作用,“一次計算出不同聚合鍵組合的結果”
-
超級分組記錄(super group row)
- GROUP BY () 表示沒有聚合鍵,也就相當於沒有 GROUP BY 子句(這時會得到全部資料的合計行的記錄)
-
將“登記日期”新增到聚合鍵當中
-- 使用ROLLUP同時得出合計和小計 。 SELECT product_type, SUM(sale_price) AS sum_price FROM Product GROUP BY ROLLUP(product_type); -- 在GROUP BY中新增“登記日期”(不使用ROLLUP) SELECT product_type, regist_date, SUM(sale_price) AS sum_price FROM Product GROUP BY product_type, regist_date; -- 在GROUP BY中新增“登記日期”(使用ROLLUP) SELECT product_type, regist_date, SUM(sale_price) AS sum_price FROM Product GROUP BY ROLLUP(product_type, regist_date);
GROUPING函式——讓NULL更加容易分辨
- 用來判斷超級分組記錄的 NULL 的 特定函式 —— GROUPING 函式
- 該函式在其引數列的值為超級分組記錄 所產生的 NULL 時返回 1,其他情況返回0
- 使用GROUPING函式能夠簡單地分辨出原始資料中的NULL和超級分組記錄中的NULL
-- 使用GROUPING函式來判斷NULL SELECT GROUPING(product_type) AS product_type, GROUPING(regist_date) AS regist_date, SUM(sale_price) AS sum_price FROM Product GROUP BY ROLLUP(product_type, regist_date); -- 在超級分組記錄的鍵值中插入恰當的字串 SELECT CASE WHEN GROUPING(product_type) = 1 THEN '商品種類 合計' ELSE product_type END AS product_type, CASE WHEN GROUPING(regist_date) = 1 THEN '登記日期 合計' ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date, SUM(sale_price) AS sum_price FROM Product GROUP BY ROLLUP(product_type, regist_date);
CUBE——用資料來搭積木
- 所謂 CUBE,就是將 GROUP BY 子句中聚合鍵的“所有可能的組合” 的彙總結果集中到一個結果中
- 組合的個數就是 2n(n 是聚合鍵的 個數)
- 可以把CUBE理解為將使用聚合鍵進行切割的模組堆積成一個立方體
-- 使用CUBE取得全部組合的結果 SELECT CASE WHEN GROUPING(product_type) = 1 THEN '商品種類 合計' ELSE product_type END AS product_type, CASE WHEN GROUPING(regist_date) = 1 THEN '登記日期 合計' ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date, SUM(sale_price) AS sum_price FROM Product GROUP BY CUBE(product_type, regist_date);
GROUPING SETS——取得期望的積木
- 該運算子可以用於從 ROLLUP 或者 CUBE 的結果中取出部分記錄
-- 使用GROUPING SETS取得部分組合的結果 SELECT CASE WHEN GROUPING(product_type) = 1 THEN '商品種類 合計' ELSE product_type END AS product_type, CASE WHEN GROUPING(regist_date) = 1 THEN '登記日期 合計' ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date, SUM(sale_price) AS sum_price FROM Product GROUP BY GROUPING SETS (product_type, regist_date);