這代表:即使被掃描的索引範圍與 order by 子句的順序完全相反,管線化 order by 依然可能成立。雖然 order by 中的 ASC / DESC 修飾詞可能阻斷管線化執行,多數資料庫都提供簡單的方式調整索引順序,讓索引重新可用。
反向索引掃描#
以下查詢取回「昨天以來的銷售」,依日期與 PRODUCT_ID 遞減排序:
SELECT sale_date, product_id, quantity
FROM sales
WHERE sale_date >= TRUNC(sysdate) - INTERVAL '1' DAY
ORDER BY sale_date DESC, product_id DESC;---------------------------------------------------------------
|Id |Operation | Name | Rows | Cost |
---------------------------------------------------------------
| 0 |SELECT STATEMENT | | 320 | 300 |
| 1 | TABLE ACCESS BY INDEX ROWID | SALES | 320 | 300 |
|*2 | INDEX RANGE SCAN DESCENDING | SALES_DT_PR | 320 | 4 |
---------------------------------------------------------------
圖 6.2:反向索引掃描
資料庫用索引樹找到最後一個匹配條目,再沿葉節點鏈「往上」走。這正是資料庫要用雙向鏈結串列來串接葉節點的原因。
當然,關鍵前提是:被掃描的索引範圍與 order by 所需順序恰好相反。
混用 ASC 與 DESC 時的斷裂#
下面這段就不滿足前提,因為它在 order by 中混用了 ASC 與 DESC:
SELECT sale_date, product_id, quantity
FROM sales
WHERE sale_date >= TRUNC(sysdate) - INTERVAL '1' DAY
ORDER BY sale_date ASC, product_id DESC;查詢必須先交出昨天的銷售(依 PRODUCT_ID 遞減),再交出今天的銷售(同樣依 PRODUCT_ID 遞減)。

圖 6.3:不可能的管線化 order by——資料庫得在索引掃描中「跳躍」
索引中並沒有「從昨天最小的
PRODUCT_ID連到今天最大的PRODUCT_ID」的連結。資料庫因此無法用這個索引避開明確排序。
解法:在索引定義中指定方向#
多數資料庫都提供簡單的方式把索引順序調成與 order by 一致——在索引宣告中使用 ASC / DESC 修飾詞:
DROP INDEX sales_dt_pr;
CREATE INDEX sales_dt_pr
ON sales (sale_date ASC, product_id DESC);現在索引順序與 order by 一致,排序操作再度消失:
---------------------------------------------------------------
|Id | Operation | Name | Rows | Cost |
---------------------------------------------------------------
| 0 | SELECT STATEMENT | | 320 | 301 |
| 1 | TABLE ACCESS BY INDEX ROWID | SALES | 320 | 301 |
|*2 | INDEX RANGE SCAN | SALES_DT_PR | 320 | 4 |
---------------------------------------------------------------
圖 6.4:混合順序的索引
第二欄排序方向的改變,等於把前一張圖的箭頭方向對調,使第一個箭頭的終點接上第二個箭頭的起點。索引因而具備所需順序,不再需要「跳躍」。
當 order by 中混用
ASC與DESC時,索引定義也必須照樣混用,才能用於管線化 order by。這不影響該索引對 where 子句的可用性。
什麼時候才真的需要 ASC/DESC 索引#
ASC/DESC 索引只在個別欄位需要相反方向排序時才需要。若是要把所有欄位的順序全部反轉,並不需要——資料庫本來就能反向讀取索引。
NULLS FIRST / NULLS LAST#
除了 ASC 與 DESC,SQL 標準還定義了兩個鮮為人知的 order by 修飾詞:NULLS FIRST 與 NULLS LAST。對 NULL 排序的明確控制是 SQL:2003 才「新近」加入的選用擴充,因此資料庫支援相當稀疏。
這特別令人擔憂,因為標準並未確切定義
NULL的排序位置:它只規定排序後所有NULL必須聚在一起,卻未指定該在其他條目之前或之後。嚴格說來,你其實得為 order by 中所有可能為
NULL的欄位都指定NULL排序,才能得到明確定義的行為。
各家支援狀況:
- SQL Server 2012 與 MySQL 5.6:均未實作這項選用擴充。
- Oracle Database:早在標準納入之前就支援
NULLS排序,但直到 11g 仍不接受它出現在索引定義中——因此以NULLS FIRST排序時無法做管線化 order by。 - PostgreSQL:自 8.3 起,order by 子句與索引定義兩處都支援
NULLS修飾詞。
