1. 程式人生 > 其它 >SQL基礎-第8章 SQL高階處理

SQL基礎-第8章 SQL高階處理

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;