SQL基礎-第8章 SQL高階處理
阿新 • • 發佈:2021-11-19
8-1 視窗函式
什麼是視窗函式
OLAP 是 OnLine Analytical Processing 的簡稱,意思是對資料庫資料進行實時分析處理。
視窗函式也稱為 OLAP 函式
視窗函式的語法
<視窗函式> OVER ([PARTITION BY <列清單>]
ORDER BY <排序用列清單>)
能夠作為視窗函式使用的函式
- 能夠作為視窗函式的聚合函式( SUM、 AVG、 COUNT、 MAX、 MIN)
- ANK、 DENSE_RANK、 ROW_NUMBER 等專用視窗函式
語法的基本使用方法——使用RANK函式
視窗函式兼具分組和排序兩種功能。
通過PARTITION BY分組後的記錄集合稱為“視窗”
-- 根據不同的商品種類,按照銷售單價從低到高的順序建立排序表
SELECT product_name, product_type, sale_price,
RANK () OVER (PARTITION BY product_type
ORDER BY sale_price) AS ranking
FROM Product;
無需指定PARTITION-BY
-- 不指定PARTITION BY SELECT product_name, product_type, sale_price, RANK () OVER (ORDER BY sale_price) AS ranking FROM Product; -- 比較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;
專用視窗函式的種類
- RANK函式
- 計算排序時,如果存在相同位次的記錄,則會跳過之後的位次。
- 例)有 3 條記錄排在第 1 位時:1 位、1 位、1 位、4 位……
- DENSE_RANK函式
- 同樣是計算排序,即使存在相同位次的記錄,也不會跳過之後的位次。
- 例)有 3 條記錄排在第 1 位時:1 位、1 位、1 位、2 位……
- ROW_NUMBER函式
- 賦予唯一的連續位次。
- 例)有 3 條記錄排在第 1 位時:1 位、2 位、3 位、4 位……
由於專用視窗函式無需引數,因此通常括號中都是空的
視窗函式的適用範圍
原則上視窗函式只能在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;
-- 指定“最靠近的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
將聚合函式作為視窗函式使用時,會以當前記錄為基準來決定彙總物件的記錄。
-- 無法保證如下SELECT語句的結果的排列順序
SELECT product_name, product_type, sale_price,
RANK () OVER (ORDER BY sale_price) AS ranking
FROM Product;
8-2 GROUPING運算子
同時得到合計行
-- 使用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;
ROLLUP——同時得出合計和小計
超級分組記錄預設使用NULL作為聚合鍵。
-- 使用ROLLUP同時得出合計和小計
SELECT product_type, SUM(sale_price) AS sum_price
FROM Product
GROUP BY product_type WITH ROLLUP;
-- 在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 product_type, regist_date WITH ROLLUP;
GROUPING函式——讓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);
使用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 product_type, regist_date WITH ROLLUP;
-- 在超級分組記錄的鍵值中插入恰當的字串
SELECT CASE WHEN GROUPING(product_type) = 1
THEN '商品種類 合計'
ELSE product_type END AS product_type,
CASE WHEN GROUPING(regist_date) = 1
THEN '登記日期 合計'
ELSE regist_date END AS regist_date,
SUM(sale_price) AS sum_price
FROM Product
GROUP BY product_type, regist_date WITH ROLLUP;
練習題
8.1 請說出針對本章中使用的 Product(商品)表執行如下 SELECT 語句所能得到的結果。
SELECT product_id, product_name, sale_price,
MAX(sale_price) OVER (ORDER BY product_id) AS current_max_price
FROM Product;
本題中 SELECT 語句的含義是“按照商品編號(product_id)的升序進行排序,
計算出截至當前行的最高銷售單價”。因此,在顯示出最高銷售單價的同時,視窗函
數的返回結果也會變化。
8.2 繼續使用Product表,計算出按照登記日期( regist_date)升序進行排列的各日期的銷售單價( sale_price)的總額。排序是需要將登記日期為 NULL 的“運動 T 恤”記錄排在第 1 位(也就是將其看作比其他日期都早)。
①和②兩種方法都可以實現。
① regist_date 為 NULL 時,顯示“1 年 1 月 1 日”
SELECT regist_date, product_name, sale_price,
SUM(sale_price)
OVER (ORDER BY COALESCE(regist_date, CAST('0001-01-01' AS DATE)))
AS current_sum_price
FROM Product;
② regist_date 為 NULL 時,將該記錄放在最前顯示
SELECT regist_date, product_name, sale_price,
SUM(sale_price)
OVER (order by IF(ISNULL(regist_date),0,1), regist_date)
AS current_sum_price
FROM Product;