谓词推送:让未合并的视图也能“飞”起来

很多博客都在讲“视图合并”,但真实世界里,UNION、GROUP BY、ROWNUM 等语法让合并直接失败。此时,谓词推送(Predicate Pushdown)就成了救命稻草:把上层 WHERE 条件推到子查询,提前过滤数据,甚至还能用到索引。

实验环境 • Oracle 19c • 表 hr.contract_workers:只有 3 行,department_id 上建了索引 • 视图 all_employees_vw:employees UNION contract_workers

动手实验

  1. 建表插数据
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);
  1. 创建视图(无法合并)
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);
  1. 查询视图
SELECT last_name
FROM   all_employees_vw
WHERE department_id = 50;
  1. 查看执行计划
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 被成功下推。

  1. 关闭谓词推送验证(可选)
ALTER SESSION SET "_PUSH_JOIN_UNION_VIEW" = FALSE;

再跑执行计划,会发现两个分支都退化为 FULL TABLE SCAN,性能瞬间回到解放前。

技术要点小结

  1. 视图合并失败 ≠ 性能必差,谓词推送同样能让优化器走到索引。
  2. 触发条件:子查询块中必须能安全地应用谓词(无 ROWNUM、无聚合等破坏语义的操作)。
  3. 查看是否推送成功:直接看执行计划里每个子分支是否出现了 INDEX RANGE SCANFILTER 对应谓词。

最佳实践 • 给视图引用的列建索引,推送才有意义。 • 用 EXPLAIN PLANDBMS_XPLAN.DISPLAY_CURSOR 验证;如果发现全表扫描,考虑重写 SQL 或调整隐藏参数(测试环境)。 • 12c 以后,可配合 INLINE Hint 强制合并,不行再依赖推送。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部