視窗函式(window function)提供了另一種在 SQL 中實作分頁的方式。這個方法很有彈性,而且最重要的是——符合標準。
但只有 SQL Server 與 Oracle 能把它用於管線化的 top-N 查詢。
- PostgreSQL:抓夠列數後不會中止索引掃描,因此執行這類查詢效率極差。
- MySQL:完全不支援視窗函式。
用 ROW_NUMBER 分頁#
SELECT *
FROM ( SELECT sales.*
, ROW_NUMBER() OVER (ORDER BY sale_date DESC
, sale_id DESC) rn
FROM sales
) tmp
WHERE rn between 11 and 20
ORDER BY sale_date DESC, sale_id DESC;ROW_NUMBER 依 over 子句定義的排序為列編號,外層 where 子句再用這個編號把結果限制在第二頁(第 11 到 20 列)。
Oracle 的執行計畫#
Oracle 認得這個中止條件,並使用 SALE_DATE、SALE_ID 上的索引產生管線化的 top-N 行為:
---------------------------------------------------------------
|Id | Operation | Name | Rows | Cost |
---------------------------------------------------------------
| 0 | SELECT STATEMENT | |1004K | 36877|
|*1 | VIEW | |1004K | 36877|
|*2 | WINDOW NOSORT STOPKEY | |1004K | 36877|
| 3 | TABLE ACCESS BY INDEX ROWID | SALES |1004K | 36877|
| 4 | INDEX FULL SCAN DESCENDING | SL_DTID |1004K | 2955|
---------------------------------------------------------------
Predicate Information:
1 - filter("RN">=11 AND "RN"<=20)
2 - filter(ROW_NUMBER() OVER (
ORDER BY "SALE_DATE" DESC, "SALE_ID" DESC )<=20)WINDOW NOSORT STOPKEY 這個操作說明了兩件事:
NOSORT:沒有排序操作。STOPKEY:抵達上界時中止執行。
考慮到這些被中止的操作是以管線化方式執行的,這代表這段查詢與前一節的偏移法一樣有效率。
不過,視窗函式的真正強項並不是分頁,而是分析型計算。如果你從未用過視窗函式,絕對值得花幾個小時研讀相關文件。