在 Oracle 資料庫中,ORDER BY 子句預設將 NULL 視為最大值,導致升冪排序時空值出現在結果集末尾。然而許多業務場景(如優先顯示未分類項目、待處理任務置頂)要求 NULL 值優先顯示。單純的 ORDER BY column_name 無法滿足此需求。
透過 UNION 將空值與非空值分離查詢,再配合 ROWNUM 合成排序序號:
SELECT * FROM (
-- 第一段:NULL 值優先,賦予較小序號
SELECT a.*, ROWNUM AS my_sys_rownum FROM (
SELECT deptid, NVL(BDZNAME,' '), NVL(VOLLEVEL,'0'), ZBRL,
NVL(ZBTS,'0'), NVL(FZR,'0'), NVL(DEPTIDDES,' '),
NVL(TEL,' '), NVL(RUNSTATEDES,' '), NVL(ADDRESS,' '), BDZID
FROM V_BDZ
WHERE ROWNUM < 2000 AND ZBRL IS NULL
) a
UNION
-- 第二段:非 NULL 值按業務欄位排序,序號後移
SELECT b.*, ROWNUM + (SELECT COUNT(*) FROM (
SELECT deptid FROM V_BDZ WHERE ROWNUM < 2000 AND ZBRL IS NULL
)) AS my_sys_rownum FROM (
SELECT deptid, NVL(BDZNAME,' '), NVL(VOLLEVEL,'0'), ZBRL,
NVL(ZBTS,'0'), NVL(FZR,'0'), NVL(DEPTIDDES,' '),
NVL(TEL,' '), NVL(RUNSTATEDES,' '), NVL(ADDRESS,' '), BDZID
FROM V_BDZ
WHERE ROWNUM < 2000 AND ZBRL IS NOT NULL
ORDER BY ZBRL
) b
) ORDER BY my_sys_rownum DESC;
缺點分析: SQL 複雜度高、維護困難、效能開銷大(多次掃描、排序運算)、難以利用索引優化。
利用 ORDER BY 子句支援運算表達式的特性,將 NULL 轉換為比正常業務值更小的替代值:
-- 假設業務值均為正數,使用 -1 作為替代值
SELECT * FROM table_name ORDER BY NVL(numeric_column, -1);
-- 使用 ASCII 碼最小的字元(如空格或特殊符號)
SELECT * FROM table_name ORDER BY NVL(varchar_column, ' ');
-- 使用極早日期作為替代值
SELECT * FROM table_name ORDER BY NVL(date_column, TO_DATE('0001-01-01','YYYY-MM-DD'));
現代 Oracle 版本原生支援 ANSI SQL 標準的空值排序控制,語法簡潔且語義明確:
-- NULL 值優先(升冪預設為 LAST,降冪預設為 FIRST)
SELECT * FROM table_name ORDER BY column_name ASC NULLS FIRST;
-- NULL 值最後
SELECT * FROM table_name ORDER BY column_name ASC NULLS LAST;
| 方案 | 適用版本 | 可讀性 | 索引利用 | 推薦指數 |
|---|---|---|---|---|
| UNION 分段法 | 所有版本 | 低 | 差 | ★☆☆☆☆ |
| NVL 轉換法 | 所有版本 | 中 | 中(需函數索引) | ★★★★☆ |
| NULLS FIRST/LAST | 10g 以上 | 高 | 佳(CBO 智能優化) | ★★★★★ |
NULLS FIRST/LAST 標準語法(Oracle 10g+ 環境)NVL(column, substitute_value) 方案,替代值須小於所有合法業務值CREATE INDEX idx_col_nulls ON table(NVL(column, -1))