一、为什么需要视图合并 当 SQL 语句引用视图时,Oracle 优化器有两种思路:
- 把视图当成一个黑盒,先单独为它生成执行计划(子计划),再把子计划嵌入到外层查询;
- 把视图里的表、谓词直接“拉平”到外层查询块,跟其它表一起重新做 Join Re-order、Access Path 评估。
第 2 种就是「视图合并(View Merging)」。合并后优化器拥有了更大的搜索空间,常常带来更优的执行计划——尤其是能让 Join Elimination、Index Nested Loop 等机会浮现出来。
二、两种场景:简单 vs. 复杂
类型 | 典型视图结构 | 合并收益 | 常见阻碍 |
---|---|---|---|
简单视图合并 | SELECT-PROJECT-JOIN(无 GROUP BY / DISTINCT / OUTER JOIN 等特殊构造) | 几乎总是收益为正 | 视图含聚合、集合运算、标量子查询;出现在半连接/反连接右侧;外表含 PL/SQL 函数;外连接限制 |
复杂视图合并 | GROUP BY、DISTINCT、CONNECT BY、MODEL 等 | 可能降低也可能提高成本,需以 CBO 为准 | 外表缺乏 rowid/唯一键;视图含 GROUPING SETS/ROLLUP/PIVOT;MODEL 子句;出现在 CONNECT BY 查询块 |
三、实战案例
- 简单视图合并 原查询:
SELECT e.first_name, e.last_name, v.street_address, v.postal_code
FROM employees e,
(SELECT d.department_id, l.street_address, l.postal_code
FROM departments d, locations l
WHERE d.location_id = l.location_id) v
WHERE v.department_id = e.department_id
AND e.last_name = 'Smith';
合并后:
SELECT e.first_name, e.last_name, l.street_address, l.postal_code
FROM employees e, departments d, locations l
WHERE d.location_id = l.location_id
AND d.department_id = e.department_id
AND e.last_name = 'Smith';
执行计划对比: • 未合并:Hash Join → VIEW → Hash Join(成本 7) • 合并后:三层嵌套循环,借助 EMP_NAME_IX、DEPT_ID_PK、LOC_ID_PK 索引(成本 4)
- 复杂视图合并(含 GROUP BY) 原查询:
SELECT c.cust_id, c.cust_first_name, c.cust_last_name
FROM customers c, products p,
(SELECT SUM(quantity_sold) total, cust_id, prod_id
FROM sales
GROUP BY cust_id, prod_id) v
WHERE c.country_id = 'US'
AND c.cust_id = v.cust_id
AND v.prod_id = p.prod_id
AND p.prod_name = 'T3 Faux Fur-Trimmed Sweater'
AND v.total > 100;
合并后(省略投影视图):
SELECT c.cust_id, c.cust_first_name, c.cust_last_name
FROM customers c, products p, sales s
WHERE ...
GROUP BY s.cust_id, s.prod_id, c.rowid, p.rowid, ...
HAVING SUM(s.quantity_sold) > 100;
CBO 发现:先过滤 customers、products,再与 sales 做 Join,最后做 GROUP BY 比「先 GROUP BY 整张 sales 表」更便宜,于是选择合并。
四、控制与安全
- 初始化参数 OPTIMIZER_SECURE_VIEW_MERGING = TRUE(默认)时,数据库会额外检查视图合并与谓词推送是否会违背视图创建者的安全意图。
- 权限 • MERGE VIEW:允许针对特定视图跳过安全检查。 • MERGE ANY VIEW:允许某用户所有视图都跳过安全检查。
- Hint
MERGE
/NO_MERGE
可以强制或阻止合并(仅当合并/不合并由于有效性而非成本被拒绝时,hint 才能生效)。
五、快速诊断:如何确认是否发生了合并
EXPLAIN PLAN 或 dbms_xplan.display_cursor: • 看不到 VIEW 操作符 → 已合并; • 看到 VIEW 但名字是 VM_NWVW_1
、VW_GBC_*
之类 → 复杂视图合并后留下的「投影视图」,表示 GROUP BY/DISTINCT 被延迟到 Join 之后执行; • 看到 VIEW 且名字即原始视图 → 未合并。
六、小结
- 简单视图合并几乎总做;复杂视图合并由 CBO 根据成本决定。
- 合并失败常见原因:聚合、外连接、集合运算、半/反连接、PL/SQL 函数等。
- 借助 hint、MERGE VIEW/MERGE ANY VIEW 权限、OPTIMIZER_SECURE_VIEW_MERGING 参数,开发者可灵活控制。
- 通过 EXPLAIN PLAN 检查是否出现 VIEW 操作符,即可快速验证合并结果。