如果我截断 table,Oracle 会再次进行硬解析吗?

Does Oracle go for hard parse again if I truncate the table?

如果我删除了 table 中的大部分行,或者如果我截断了它,它是否会再次进行硬解析,即使它之前是软解析?我很困惑,因为统计数据会改变但查询不会改变。 此外,如果我截断 table 并用相同的数据再次填充它是否会进行硬解析? (查询在所有情况下都是相同的)

解析对任何 dml 都一无所知activity。您可以在文档中阅读所有相关内容。 SQL 处理概览:https://docs.oracle.com/database/121/TGSQL/tgsql_sqlproc.htm#TGSQL178

共享池检查(解析的最后阶段,确定硬与软的位置:https://docs.oracle.com/database/121/TGSQL/tgsql_sqlproc.htm#GUID-BFF0B26C-0A5D-4F79-B01E-8E1C4064A6AD

截断会导致额外的硬解析,但删除不会。

为什么不同?

截断命令不仅会删除所有行,它实际上会破坏并重新创建 table。截断可能会使依赖索引无效,创建新段并更改 DBA_OBJECTS.DATA_OBJECT_ID。任何引用被截断的 table 的语句都将立即失效。

删除可能不是很危险,并且不会直接使引用 table 的语句失效。随着时间的推移,如果有足够的删除,自动优化器统计作业将检测到发生了重大变化,并将 re-gather 统计 table 并使相关查询无效。但即使这种无效也可能不会立即发生,因为统计信息收集并不总是使所有相关查询无效。

测试用例

为了确定,我们可以测量硬解析的数量。下面的测试用例表明截断会生成硬解析而删除不会。 (但是由于某些无法解释的原因可能会发生硬解析,例如从共享池中删除查询。所以不要指望数字是准确的。)

首先,创建一个table来保存将被删除的行,并创建一个函数来获取当前会话中硬解析的数量。

create table test1(a number);
insert into test1 select level from dual connect by level <= 100000;
commit;

create or replace function get_hard_parse_count return number authid current_user is
    v_value number;
begin
    execute immediate
    q'[
        select value
        from v$mystat
        join v$statname
            on v$mystat.statistic# = v$statname.statistic#
        where name = 'parse count (hard)'
    ]'
    into v_value;

    return v_value;
end;
/   

下面的代码删除所有行并计算行数,重复 100 次。但是硬解析的数量几乎没有增加。

--Count before: 751
select get_hard_parse_count from dual;

declare
    v_count number;
begin
    for i in 1 .. 100 loop
        execute immediate 'delete from test1';
        rollback;
        execute immediate 'select count(*) from test1';
    end loop;
end;
/

--Count after: 759
select get_hard_parse_count from dual;

下面的代码截断而不是删除,并且硬解析的数量增加了大量:

--Count before: 760
select get_hard_parse_count from dual;

declare
    v_count number;
begin
    for i in 1 .. 100 loop
        execute immediate 'truncate table test1';
        rollback;
        execute immediate 'select count(*) from test1';
    end loop;
end;
/

--Count after: 1,162
select get_hard_parse_count from dual;