是否有生成执行计划而忽略共享池中现有计划的提示?

Is there a hint to generate execution plan ignoring the existing one from shared pool?

是否有生成执行计划的提示,忽略共享池中的现有计划?

我认为没有任何提示表明 Oracle 每次 运行 查询时都会找到一个新的执行计划。

这是我们想要的 select * from mytable where is_active = :active,其中 is_active 为 1 表示极少数行,0 表示可能有数十亿行。我们需要 :active = 1 的索引访问和 :active = 0 的完整 table 扫描。两个不同的计划。

据我所知,Oracle在以后的版本中使用了绑定变量窥视,所以看一下统计数据,它确实针对不同的绑定变量内容得出了不同的执行计划。但在旧版本中它没有,因此我们需要一些提示,在那里说 "make a new plan"。

Oracle 只为完全 相同的查询重新使用执行计划。只需添加一个空白就可以得到一个新的计划。因此,解决方案可能是每次您想 运行 生成查询,并在评论中包含一个随机数:

select /* 1234567 */ * from mytable where is_active = :active;

或者不使用绑定变量,如果这是您要解决的问题:

select * from mytable where is_active = 0;

select * from mytable where is_active = 1;

没有创建忽略共享池中计划的执行计划的提示。表述这个问题的一种更常见的方式是:如何让 Oracle 始终执行硬解析?

在一些奇怪的情况下需要这种行为。充分解释您需要它的原因会很有帮助,因为解决方案因您需要它的原因而异。

  1. 奇怪的性能问题。 Oracle 在第一个 运行 之后对 SQL 语句执行一些动态重新优化,例如自适应游标共享和基数反馈。在极少数情况下,当这些功能适得其反时,您可能希望禁用它们。
  2. 动态查询。您有一个动态查询使用 Oracle 数据磁带在解析步骤中获取数据,但 Oracle 不会执行解析步骤,因为查询看起来是静态的甲骨文。
  3. 误解。出了点问题,这是一个 XY 问题。

解决方案

解决此问题的最简单方法是使用 Thorsten Kettner 每次更改查询的解决方案。

如果这不是一个选项,第二个最简单的解决方案是从共享池中刷新查询,如下所示:

--This only works one node at a time.
begin
    for statements in
    (
        select distinct address, hash_value
        from gv$sql
        where sql_id = '33t9pk44udr4x'
        order by 1,2
    ) loop
        sys.dbms_shared_pool.purge(statements.address||','||statements.hash_value, 'C');
    end loop;
end;
/

如果您无法控制 SQL,并且需要使用副作用式解决方案来解决问题,Jonathan Lewis 和 Randolf Geist 有一个 solution using Virtual Private Database,它添加了一个独特的谓词针对特定 table 上的每个 SQL 语句。你问了一些奇怪的事情,这是一个奇怪的解决方案。系好安全带。

-- Create a random predicate for each query on a specific table.
create table hard_parse_test_rand as
select * from all_objects
where rownum <= 1000;

begin
  dbms_stats.gather_table_stats(null, 'hard_parse_test_rand');
end;
/

create or replace package pkg_rls_force_hard_parse_rand is
  function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2;
end pkg_rls_force_hard_parse_rand;
/

create or replace package body pkg_rls_force_hard_parse_rand is
  function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2
  is
    s_predicate varchar2(100);
    n_random pls_integer;
  begin
    n_random := round(dbms_random.value(1, 1000000));
    -- s_predicate := '1 = 1';
    s_predicate := to_char(n_random, 'TM') || ' = ' || to_char(n_random, 'TM');
    -- s_predicate := 'object_type = ''TABLE''';
    return s_predicate;
  end force_hard_parse;
end pkg_rls_force_hard_parse_rand;
/

begin
  DBMS_RLS.ADD_POLICY (USER, 'hard_parse_test_rand', 'hard_parse_policy', USER, 'pkg_rls_force_hard_parse_rand.force_hard_parse', 'select');
end;
/

alter system flush shared_pool;

您可以通过 运行多次使用相同的查询来查看硬解析:

select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;

现在每次执行在 GV$SQL 中有三个条目。虚拟专用数据库中存在一些奇怪的行为,多次解析查询,即使最终文本看起来相同。

select *
from gv$sql
where sql_text like '%hard_parse_test_rand%'
    and sql_text not like '%quine%'
order by 1;