/*+ MATERIALIZE */ 优化器提示在 WITH 子句中的使用验证

Oracle /*+ MATERIALIZE */ 优化器提示在 WITH 子句中的使用验证

概述

/*+ MATERIALIZE */ 是 Oracle 数据库的优化器提示(Hint),核心作用是强制将 WITH 子句(公共表表达式,CTE)的查询结果物化到临时表 中。当后续查询多次引用该 CTE 时,可直接复用临时表数据,避免重复执行子查询;即使仅引用一次,也能通过该 Hint 强制触发物化行为。

测试场景与验证

场景 1:重复引用子查询(非 WITH 子句)------ 无临时表物化

当相同子查询被多次直接引用(未封装到 WITH 子句)时,Oracle 优化器不会将子查询结果物化到临时表,每次引用都会重新执行子查询。

sql 复制代码
SELECT main.cust_id, main.cust_name, main.order_summary, sub1.vip_count
  FROM (
        SELECT c1.cust_id, c1.cust_name, SUM(o.order_amount) AS order_summary
          FROM (SELECT cust_id, cust_name, cust_level
                   FROM customers
                  WHERE total_consume > 50000) c1
          LEFT JOIN orders o
            ON c1.cust_id = o.cust_id
         GROUP BY c1.cust_id, c1.cust_name) main
 CROSS JOIN (
             SELECT COUNT(*) AS vip_count
               FROM (SELECT cust_id, cust_name, cust_level
                        FROM customers
                       WHERE total_consume > 50000) c2
              WHERE c2.cust_level = 'VIP') sub1

执行计划结论 :预估执行计划中未使用临时表空间,子查询被重复执行。

在这里插入图片描述

场景 2:重复引用 WITH 子句中的 CTE------ 触发物化

将重复执行的子查询封装到 WITH 子句中,多次引用该 CTE 时,Oracle 会自动将 CTE 结果物化到临时表。

sql 复制代码
WITH c AS
 (SELECT cust_id, cust_name, cust_level
    FROM customers
   WHERE total_consume > 50000)

SELECT main.cust_id, main.cust_name, main.order_summary, sub1.vip_count
  FROM (
        -- 第一次引用 c
        SELECT c1.cust_id, c1.cust_name, SUM(o.order_amount) AS order_summary
          FROM c c1
          LEFT JOIN orders o
            ON c1.cust_id = o.cust_id
         GROUP BY c1.cust_id, c1.cust_name) main
 CROSS JOIN (
             -- 第二次引用 c
             SELECT COUNT(*) AS vip_count
               FROM c c2
              WHERE c2.cust_level = 'VIP') sub1

执行计划结论 :CTE 的结果集被物化到临时表中,后续引用直接复用临时表数据。

场景 3:单次引用 WITH 子句中的 CTE------ 不触发物化

若 WITH 子句中的 CTE 仅被引用一次,Oracle 优化器默认不会将结果集物化到临时表,而是直接执行子查询。

复制代码
WITH c AS
 (SELECT cust_id, cust_name, cust_level
    FROM customers
   WHERE total_consume > 50000)

SELECT main.cust_id, main.cust_name, main.order_summary
  FROM (
        -- 仅一次引用 c
        SELECT c1.cust_id, c1.cust_name, SUM(o.order_amount) AS order_summary
          FROM c c1
          LEFT JOIN orders o
            ON c1.cust_id = o.cust_id
         GROUP BY c1.cust_id, c1.cust_name) main

执行计划结论:预估执行计划中无临时表物化行为,CTE 子查询直接执行。

场景 4:单次引用 +/*+ MATERIALIZE */ Hint------ 强制物化

在 WITH 子句的 CTE 中添加/*+ MATERIALIZE */ Hint,即使 CTE 仅被引用一次,也能强制 Oracle 将结果集物化到临时表。

测试 SQL

sql

复制代码
WITH c AS
 (SELECT /*+ MATERIALIZE */ cust_id, cust_name, cust_level
    FROM customers
   WHERE total_consume > 50000)

SELECT main.cust_id, main.cust_name, main.order_summary
  FROM (
        -- 仅一次引用 c
        SELECT c1.cust_id, c1.cust_name, SUM(o.order_amount) AS order_summary
          FROM c c1
          LEFT JOIN orders o
            ON c1.cust_id = o.cust_id
         GROUP BY c1.cust_id, c1.cust_name) main

执行计划结论 :CTE 结果集被强制物化到临时表中。

场景 5:Hint 直接写在普通子查询中 ------ 无效

/*+ MATERIALIZE */ Hint 直接添加到非 WITH 子句的普通子查询中,无法触发物化行为。

执行计划结论 :实验验证该方式无效,临时表物化未发生。

三、结论

  1. /*+ MATERIALIZE */ 仅对WITH 子句内的 CTE 生效,直接写在普通子查询中无物化效果;
  2. WITH 子句中的 CTE 被多次引用 时,Oracle 会自动物化结果到临时表;仅被单次引用 时,默认不物化;
  3. 即使 CTE 仅单次引用,也可通过在 WITH 子句的 CTE 查询中添加/*+ MATERIALIZE */ Hint,强制将结果集物化到临时表,适用于优化器判断失误时,未将结果集物化到临时表的情况。
  4. 需要复用子查询结果或优化执行效率的场景。
相关推荐
资深数据库专家2 小时前
EBS 中出现的“销售退货单库存已回冲,但生产成本未变化”的问题
人工智能·经验分享·oracle·微信公众平台·新浪微博
Apple_羊先森3 小时前
ORACLE数据库巡检SQL脚本--13、临时表空间使用率
数据库·sql·oracle
数据库生产实战4 小时前
Oracle隐藏参数_fix_control和_optimizer_improve_selectivity设置方法,如何用于规避性能问题?你值得看看!
数据库·oracle
胡斌附体4 小时前
oracle-xe创建
数据库·oracle
世界尽头与你13 小时前
(修复方案)CVE-2023-22047: Oracle PeopleSoft Enterprise PeopleTools 未授权访问漏洞
数据库·安全·oracle·渗透测试
ActionTech1 天前
MySQL 用好 Optimizer Trace,深刻理解 SQL 优化过程!
sql·mysql·oracle
多多*1 天前
Mysql数据库相关 事务 MVCC与锁的爱恨情仇 锁的层次架构 InnoDB锁分析
java·数据库·windows·sql·oracle·面试·哈希算法
Monkey的自我迭代1 天前
实战项目数据桥agent复盘
数据库·python·oracle
远方16091 天前
112-Oracle database 26ai下载和安装环境准备
大数据·数据库·sql·oracle·database