使用sql_profile固定执行计划(脚本分享)

       生产上经常遇到执行计划“突变”的问题,针对这种问题 常规的解决就是收集统计信息。如果相关表的segment很大,收集统计信息需要很长时间。在应急的情况下快速解决问题,只需要绑定这个SQL突变前的执行计划,即可!下面分享一个脚本 用于绑定执行计划:直接调用存储过程,传入的参数有三个 

ORIGINAL_SQL_ID           需要固定执行计划的SQL
MODIFIED_SQL_ID           用来固定执行计划的SQL(一般和第一个参数传值相同)
PLAN_HASH_VALUE1      用来固定执行计划的PLAN_HASH_VALUE

create or replace procedure p_sql_profile(ORIGINAL_SQL_ID in varchar2,
                                          MODIFIED_SQL_ID in varchar2,
                                          PLAN_HASH_VALUE1 in number) is
  sql_text  CLOB := NULL;
  other_xml CLOB := NULL;
  signature NUMBER := NULL;
  name      VARCHAR2(30) := NULL;

  l_sql_text VARCHAR2(32767);

  h           SYS.SQLPROF_ATTR := SYS.SQLPROF_ATTR();
  idx         INTEGER := 0;
  l_pos       NUMBER;
  l_hint      VARCHAR2(32767);
  description VARCHAR2(500);
  --SQLPROF_ATTR
PROCEDURE add_hint(p_hint IN VARCHAR2) IS
  BEGIN
    idx := idx + 1;

    DBMS_OUTPUT.PUT_LINE(LPAD(idx, 4, '0') || ' ' || p_hint);
    h.EXTEND;
    h(idx) := p_hint;
  END add_hint;

BEGIN
  -- get sql_text from memory
  FOR i IN (SELECT DISTINCT piece, sql_text
              FROM gv$sqltext_with_newlines
             WHERE sql_id = ORIGINAL_SQL_ID --o
             ORDER BY 1, 2) LOOP
    IF sql_text IS NULL THEN
      DBMS_LOB.CREATETEMPORARY(sql_text, TRUE);
      DBMS_LOB.OPEN(sql_text, DBMS_LOB.LOB_READWRITE);
    END IF;
    l_sql_text := REPLACE(i.sql_text, CHR(00), ' ');
    DBMS_LOB.WRITEAPPEND(sql_text, LENGTH(l_sql_text), l_sql_text);
  END LOOP;
  IF sql_text IS NOT NULL THEN
    DBMS_LOB.CLOSE(sql_text);
  END IF;
  -- get sql_text from awr
  IF sql_text IS NULL OR NVL(DBMS_LOB.GETLENGTH(sql_text), 0) = 0 THEN
    SELECT REPLACE(sql_text, CHR(00), ' ')
      INTO sql_text
      FROM dba_hist_sqltext
     WHERE sql_id = ORIGINAL_SQL_ID --o
       AND sql_text IS NOT NULL
       AND ROWNUM = 1;
  END IF;

  -- get other_xml from memory
  FOR i IN (SELECT other_xml
              FROM gv$sql_plan
             WHERE sql_id = MODIFIED_SQL_ID --m
               AND plan_hash_value = PLAN_HASH_VALUE1 --p
               AND other_xml IS NOT NULL
             ORDER BY child_number, id) LOOP
    other_xml := i.other_xml;
  END LOOP;
  dbms_output.put_line('other_xml1 is' || other_xml);

  -- get other_xml from awr
  IF other_xml IS NULL OR NVL(DBMS_LOB.GETLENGTH(other_xml), 0) = 0 THEN
    FOR i IN (SELECT other_xml
                FROM dba_hist_sql_plan
               WHERE sql_id = MODIFIED_SQL_ID --m
                 AND plan_hash_value = PLAN_HASH_VALUE1 --p 
                 AND other_xml IS NOT NULL
               ORDER BY id) LOOP
      other_xml := i.other_xml;
    END LOOP;
  END IF;
  dbms_output.put_line('other_xml2 is' || other_xml);

  --hint
  add_hint('BEGIN_OUTLINE_DATA');
  FOR i IN (SELECT /*+ opt_param('parallel_execution_enabled', 'false') */
             SUBSTR(EXTRACTVALUE(VALUE(d), '/hint'), 1, 4000) hint
              FROM TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE(other_xml),
                                             '/*/outline_data/hint'))) d) LOOP
    l_hint := i.hint;
    WHILE NVL(LENGTH(l_hint), 0) > 0 LOOP
      IF LENGTH(l_hint) <= 500 THEN
        add_hint(l_hint);
        l_hint := NULL;
      ELSE
        l_pos := INSTR(SUBSTR(l_hint, 1, 500), ' ', -1);
        add_hint(SUBSTR(l_hint, 1, l_pos));
        l_hint := '   ' || SUBSTR(l_hint, l_pos);
      END IF;
    END LOOP;
  
  END LOOP;
  add_hint('END_OUTLINE_DATA');

  signature   := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_text);
  name        := UPPER(original_sql_id) || '_' || PLAN_HASH_VALUE1;
  description := UPPER('original:' || original_sql_id || ' modified:' ||
                       modified_sql_id || ' phv:' || PLAN_HASH_VALUE1 ||
                       ' signature:' || signature ||
                       ' created by coe_load_sql_profile.sql');
  -- create custom sql profile for original sql using plan from modified sql
  DBMS_SQLTUNE.IMPORT_SQL_PROFILE(sql_text    => sql_text, -- original sql
                                  profile     => h, -- plan from modified sql
                                  name        => name,
                                  description => description,
                                  category    => 'DEFAULT',
                                  validate    => TRUE,
                                  replace     => TRUE,
                                  force_match => FALSE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */);
  --异常处理
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('sql profile create filed');
end p_sql_profile;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值