很多博客都在讲“视图合并”,但真实世界里,UNION、GROUP BY、ROWNUM 等语法让合并直接失败。此时,谓词推送(Predicate Pushdown)就成了救命稻草:把上层 WHERE 条件推到子查询,提前过滤数据,甚至还能用到索引。
实验环境 • Oracle 19c • 表 hr.contract_workers:只有 3 行,department_id 上建了索引 • 视图 all_employees_vw:employees UNION contract_workers
动手实验
- 建表插数据
DROP TABLE contract_workers;
CREATE TABLE contract_workers AS
SELECT * FROM employees WHERE 1=2;
INSERT INTO contract_workers VALUES
(306,'Bill','Jones','BJONES','555.555.2000','07-JUN-02','AC_ACCOUNT',8300,0,205,110);
INSERT INTO contract_workers VALUES
(406,'Jill','Ashworth','JASHWORTH','555.999.8181','09-JUN-05','AC_ACCOUNT',8300,0,205,50);
INSERT INTO contract_workers VALUES
(506,'Marcie','Lunsford','MLUNSFORD','555.888.2233','22-JUL-01','AC_ACCOUNT',8300,0,205,110);
COMMIT;
CREATE INDEX contract_workers_index ON contract_workers(department_id);
- 创建视图(无法合并)
CREATE OR REPLACE VIEW all_employees_vw AS
(SELECT employee_id, last_name, job_id, commission_pct, department_id
FROM employees)
UNION
(SELECT employee_id, last_name, job_id, commission_pct, department_id
FROM contract_workers);
- 查询视图
SELECT last_name
FROM all_employees_vw
WHERE department_id = 50;
- 查看执行计划
EXPLAIN PLAN FOR
SELECT last_name
FROM all_employees_vw
WHERE department_id = 50;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
关键行(简化):
----------------------------------------------------------------
| Id | Operation | Name |
----------------------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | VIEW | ALL_EMPLOYEES_VW |
| 2 | UNION-ALL | |
| 3 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES |
|* 4 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX |
| 5 | TABLE ACCESS BY INDEX ROWID| CONTRACT_WORKERS |
|* 6 | INDEX RANGE SCAN | CONTRACT_WORKERS_INDEX |
----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("DEPARTMENT_ID"=50)
6 - access("DEPARTMENT_ID"=50)
亮点: • 在 UNION 的两个分支里,都出现了 INDEX RANGE SCAN,说明谓词 department_id = 50
被成功下推。
- 关闭谓词推送验证(可选)
ALTER SESSION SET "_PUSH_JOIN_UNION_VIEW" = FALSE;
再跑执行计划,会发现两个分支都退化为 FULL TABLE SCAN,性能瞬间回到解放前。
技术要点小结
- 视图合并失败 ≠ 性能必差,谓词推送同样能让优化器走到索引。
- 触发条件:子查询块中必须能安全地应用谓词(无 ROWNUM、无聚合等破坏语义的操作)。
- 查看是否推送成功:直接看执行计划里每个子分支是否出现了
INDEX RANGE SCAN
或FILTER
对应谓词。
最佳实践 • 给视图引用的列建索引,推送才有意义。 • 用 EXPLAIN PLAN
或 DBMS_XPLAN.DISPLAY_CURSOR
验证;如果发现全表扫描,考虑重写 SQL 或调整隐藏参数(测试环境)。 • 12c 以后,可配合 INLINE
Hint 强制合并,不行再依赖推送。