Oracle SQL Plan Management (SPM): Query Para Relacionar PLAN_NAME com PLAN_HASH_VALUE

SQL Plan Management (SPM) é uma feature do Oracle para controle dos planos de execução usados pelo otimizador e atua principalmente para evitar problemas com regressão de desempenho de instruções SQL. Se você ainda não conhece, recomendo a leitura do artigo do “Criando SQL Plan baselines utilizando o SQL*Plus” publicado por Alex Zaballa.

Recentemente precisei implementar a solução e uma das necessidades era obter o PLAN_HASH_VALUE para correlacionar os planos de execução existentes no SPM com outras ferramentas utilizadas no ambiente, e a view DBA_SQL_PLAN_BASELINES não ajuda muito com isso (até versão 19c). Também, cada SQL só é catalogado no SPM quando ele é executado após habilitar a feature, mas ainda é possível importar um SQL/Plano manualmente caso o mesmo ainda não tenha sido executado/utilizado desde que o parâmetro “optimizer_capture_sql_plan_baselines” tenha sido alterado para TRUE.

Neste post disponibilizo alguns scripts que precisei para conseguir esses objetivos: Relacionar SPM PLAN_NAME vs PLAN_HASH_VALUE disponível no Cursor Cache e importar um plano manualmente do histórico do AWR.

Obter PLAN_HASH_VALUE para PLAN_NAME

Usamos a function DISPLAY_SQL_PLAN_BASELINE da package DBMS_XPLAN para obter o plano de execução completo para uma determinada combinaçao de SQL_HANDLE + PLAN_NAME da view DBA_SQL_PLAN_BASELINES.

Síntaxe:

SELECT * FROM DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE('<SQL HANDLE>','<PLAN_NAME>');

A query abaixo obtém o plano completo com a DBMS_XPLAN e extrai somente o valor do atributo “Plan hash value”.

Listando todos os planos no SPM de um SQL_ID específico, com os respectivos PLAN_HASH_VALUE:

SELECT SIGNATURE, 
       SQL_HANDLE, 
       PLAN_NAME, 
       ENABLED, 
       ACCEPTED, 
       ADAPTIVE 
     ,(select replace(plan_table_output,'Plan hash value: ','')
         from dbms_xplan.display_sql_plan_baseline('' || SQL_HANDLE || '','' || PLAN_NAME || '')
        where plan_table_output like '%Plan hash value%'
       ) as PLAN_HASH_VALUE
     -- comando para obter o plano
     ,'select * from dbms_xplan.display_sql_plan_baseline(''' || SQL_HANDLE || ''',''' || PLAN_NAME || ''');' as get_full_plan
FROM DBA_SQL_PLAN_BASELINES B
where signature in (select exact_matching_signature from gv$sql where sql_id='&sqlid')
order by signature;

Listando todos os planos de um SQL_ID específico, com os respectivos PLAN_HASH_VALUE e tempo médio de execução (sub query do script coe.sql):

SET VERIFY OFF
SET LINESIZE 400
SET PAGESIZE 500
COL SIGNATURE  FORMAT 9999999999999999999999
COL SQL_HANDLE FORMAT A20
COL PLAN_NAME  FORMAT A30
COL ORIGIN     FORMAT A15 TRUNC
COL CREATED    FORMAT A20
COL MODIFIED   FORMAT A20
COL ENABLED    FORMAT A8
COL ACCEPTED   FORMAT A7
COL ADAPTIVE   FORMAT A8
COL PLAN_HASH_VALUE FORMAT A15
WITH SPM AS (
SELECT SIGNATURE, 
       SQL_HANDLE, 
       PLAN_NAME, 
       ORIGIN, 
       TO_CHAR(CREATED,'DD/MM/YYYY HH24:MI:SS') CREATED,
       TO_CHAR(LAST_MODIFIED,'DD/MM/YYYY HH24:MI:SS') AS MODIFIED, 
       ENABLED, 
       ACCEPTED, 
       ADAPTIVE 
     ,(select replace(plan_table_output,'Plan hash value: ','')
         from dbms_xplan.display_sql_plan_baseline('' || SQL_HANDLE || '','' || PLAN_NAME || '')
        where plan_table_output like '%Plan hash value%'
       ) as PLAN_HASH_VALUE
FROM DBA_SQL_PLAN_BASELINES
WHERE signature in (select exact_matching_signature from gv$sql where sql_id='&&sql_id')
)
SELECT P.* , T.AVG_ET_SECS 
  FROM SPM P,
  ( -- sub consulta do coe.sql para obter tempo medio de execucao de cada plano
    WITH
    p AS (
    SELECT plan_hash_value
      FROM gv$sql_plan
     WHERE sql_id = TRIM('&&sql_id.')
       AND other_xml IS NOT NULL
    UNION
    SELECT plan_hash_value
      FROM dba_hist_sql_plan
     WHERE sql_id = TRIM('&&sql_id.')
       AND other_xml IS NOT NULL ),
    m AS (
    SELECT plan_hash_value, SUM(elapsed_time)/SUM(executions) avg_et_secs
      FROM gv$sql
     WHERE sql_id = TRIM('&&sql_id.')
       AND executions > 0
    GROUP BY plan_hash_value ),
    a AS (
    SELECT plan_hash_value,SUM(elapsed_time_total)/SUM(executions_total) avg_et_secs
      FROM dba_hist_sqlstat
     WHERE sql_id = TRIM('&&sql_id.')
       AND executions_total > 0
    GROUP BY plan_hash_value )
    SELECT p.plan_hash_value, ROUND(NVL(m.avg_et_secs, a.avg_et_secs)/1e6, 2) avg_et_secs
      FROM p, m, a
     WHERE p.plan_hash_value = m.plan_hash_value(+)
       AND p.plan_hash_value = a.plan_hash_value(+)
) T
WHERE P.PLAN_HASH_VALUE = T.PLAN_HASH_VALUE(+);
undefine sql_id;

Como Importar Um Plano do Histórico AWR

Consulte os planos existentes no histórico do AWR para o SQL_ID específico,

A query abaixo lista os PLAN_HASH_VALUE com o tempo médio de execução e o número do maior snapshot do AWR:

select plan_hash_value, max(snap_id)-1 awr_snap, round(sum(elapsed_time_total)/sum(executions_total)/1e6,2) avg_et_secs
from dba_hist_sqlstat 
where sql_id='&sql_id' 
and executions_total > 0
group by plan_hash_value;

O script a seguir importa o plano para o SPM Baseline:

DECLARE
 vSnapMin number       := 94263;            -- valor da coluna "awr_snap" na query anterior
 vSQL_ID varchar2(200) := '57hkw363cmjbs';
 vHashPlan number      := 483801899;       -- hash_plan a ser importado, conforme query anterior
 vLoad varchar2(200);
BEGIN
 vLoad := DBMS_SPM.LOAD_PLANS_FROM_AWR(
  BEGIN_SNAP   => vSnapMin,
  END_SNAP     => vSnapMin+1,
  ENABLED      => 'NO', --> alterar para YES se quiser habilitar o plano automaticamente
  BASIC_FILTER => 'sql_id=''' || vSQL_ID || ''' and plan_hash_value=''' || vHashPlan || ''' '
);
END;
/

Referências:

207.4.6 DISPLAY_SQL_PLAN_BASELINE Function

Overview of SQL Plan Management

1 thought on “Oracle SQL Plan Management (SPM): Query Para Relacionar PLAN_NAME com PLAN_HASH_VALUE”

  1. Pingback: 3 Abordagens Diferentes Para Fixar Planos de Execução de Comandos SQL no Oracle Database – Blog do Dibiei

Leave a Comment

Scroll to Top

Discover more from Blog do Dibiei

Subscribe now to keep reading and get access to the full archive.

Continue reading