• 懒人看执行计划神器 for Oracle


    原文链接:https://www.modb.pro/db/23252?cyn (阅读原文,支持作者)

    摘要:oracle小工具

    1. 如果一个sql执行计划几十甚至上百行怎么看?本文介绍一个偷懒工具xplan包

    利用xplan包可以见到看到执行计划的顺序,如下所示
    select * from table(xplan.display_cursor(‘v$sql.sql_id’,0,‘advanced’));

    前提是需要执行xplan.package.sql,并做如下授权
    SQL> CREATE PUBLIC SYNONYM XPLAN FOR SYS.XPLAN;
    SQL> grant execute on sys.xplan to public;

    xplan.package.sql内容如下:

    SET DEFINE ON
    DEFINE _awr_start = "/*"
    DEFINE _awr_end   = "*/"
    
    
    --
    -- Supporting types for the pipelined functions...
    --
    
    
    CREATE OR REPLACE TYPE xplan_ot AS OBJECT( plan_table_output VARCHAR2(300) );
    /
    
    
    CREATE OR REPLACE TYPE xplan_ntt AS  TABLE OF xplan_ot;
    /
    
    
    --
    -- Xplan package...
    --
    
    
    CREATE OR REPLACE PACKAGE xplan AS
    
    
       FUNCTION display( p_table_name   IN VARCHAR2 DEFAULT 'PLAN_TABLE',
                         p_statement_id IN VARCHAR2 DEFAULT NULL,
                         p_format       IN VARCHAR2 DEFAULT 'TYPICAL' )
          RETURN xplan_ntt PIPELINED;
    
    
       FUNCTION display_cursor( p_sql_id          IN VARCHAR2 DEFAULT NULL,
                                p_cursor_child_no IN INTEGER  DEFAULT 0,
                                p_format          IN VARCHAR2 DEFAULT 'TYPICAL' )
          RETURN xplan_ntt PIPELINED;
    
    
    &&_awr_start
       FUNCTION display_awr( p_sql_id          IN VARCHAR2,
                             p_plan_hash_value IN INTEGER  DEFAULT NULL,
                             p_db_id           IN INTEGER  DEFAULT NULL,
                             p_format          IN VARCHAR2 DEFAULT 'TYPICAL' )
          RETURN xplan_ntt PIPELINED;
    &&_awr_end
    
    END xplan;
    /
    
    
    CREATE OR REPLACE PACKAGE BODY xplan AS
    
    
       TYPE ntt_order_map_binds IS TABLE OF VARCHAR2(100);
    
    
       TYPE aat_order_map IS TABLE OF PLS_INTEGER
          INDEX BY PLS_INTEGER;
    
    
       g_map  aat_order_map;
       g_hdrs PLS_INTEGER;
       g_len  PLS_INTEGER;
       g_pad  VARCHAR2(300);
    
    
       ----------------------------------------------------------------------------
       PROCEDURE reset_state IS
       BEGIN
          g_hdrs := 0;
          g_len  := 0;
          g_pad  := NULL;
          g_map.DELETE;
       END reset_state;
    
    
       ----------------------------------------------------------------------------
       PROCEDURE build_order_map( p_sql   IN VARCHAR2,
                                  p_binds IN ntt_order_map_binds ) IS
    
    
          TYPE rt_id_data IS RECORD
          ( id  PLS_INTEGER
          , ord PLS_INTEGER );
    
    
          TYPE aat_id_data IS TABLE OF rt_id_data
             INDEX BY PLS_INTEGER;
    
    
          aa_ids   aat_id_data;
          v_cursor SYS_REFCURSOR;
          v_sql    VARCHAR2(32767);
    
    
       BEGIN
    
    
          -- Build SQL template...
          -- ---------------------
          v_sql := 'WITH sql_plan_data AS ( ' ||
                            p_sql || '
                            )
                    ,    hierarchical_sql_plan_data AS (
                            SELECT id
                            FROM   sql_plan_data
                            START WITH id = 0
                            CONNECT BY PRIOR id = parent_id
                            ORDER SIBLINGS BY id DESC
                            )
                    SELECT id
                    ,      ROW_NUMBER() OVER (ORDER BY ROWNUM DESC) AS ord
                    FROM   hierarchical_sql_plan_data';
    
    
          -- Binds will differ according to plan type...
          -- -------------------------------------------
          CASE p_binds.COUNT
             WHEN 0
             THEN
                OPEN v_cursor FOR v_sql;
             WHEN 1
             THEN
                OPEN v_cursor FOR v_sql USING p_binds(1);
             WHEN 2
             THEN
                OPEN v_cursor FOR v_sql USING p_binds(1),
                                              TO_NUMBER(p_binds(2));
             WHEN 3
             THEN
                OPEN v_cursor FOR v_sql USING p_binds(1),
                                              TO_NUMBER(p_binds(2)),
                                              TO_NUMBER(p_binds(3));
          END CASE;
    
    
          -- Fetch the ID and order data...
          -- ------------------------------
          FETCH v_cursor BULK COLLECT INTO aa_ids;
          CLOSE v_cursor;
    
    
          -- Populate the order map...
          -- -------------------------
          FOR i IN 1 .. aa_ids.COUNT LOOP
             g_map(aa_ids(i).id) := aa_ids(i).ord;
          END LOOP;
    
    
          -- Use the map to determine padding needed to slot in our order column...
          -- ----------------------------------------------------------------------
          IF g_map.COUNT > 0 THEN
             g_len := LEAST(LENGTH(g_map.LAST) + 7, 8);
             g_pad := LPAD('-', g_len, '-');
          END IF;
    
    
       END build_order_map;
    
    
       ----------------------------------------------------------------------------
       FUNCTION prepare_row( p_curr IN VARCHAR2,
                             p_next IN VARCHAR2 ) RETURN xplan_ot IS
    
    
          v_id  PLS_INTEGER;
          v_row VARCHAR2(4000);
          v_hdr VARCHAR2(64) := '%|%Id%|%Operation%|%';
    
    
       BEGIN
    
    
          -- Intercept the plan section to include a new column for the
          -- the operation order that we mapped earlier. The plan output
          -- itself will be bound by the 2nd, 3rd and 4th dashed lines.
          -- We need to add in additional dashes, the order column heading
          -- and the order value itself...
          -- -------------------------------------------------------------
    
    
          IF p_curr LIKE '---%' THEN
    
             IF p_next LIKE v_hdr THEN
                g_hdrs := 1;
                v_row := g_pad || p_curr;
             ELSIF g_hdrs BETWEEN 1 AND 3 THEN
                g_hdrs := g_hdrs + 1;
                v_row := g_pad || p_curr;
             ELSE
                v_row := p_curr;
             END IF;
    
    
          ELSIF p_curr LIKE v_hdr THEN
    
    
             v_row := REGEXP_REPLACE(
                         p_curr, '|',
                         RPAD('|', GREATEST(g_len-7, 2)) || 'Order |',
                         1, 2
                         );
    
    
          ELSIF REGEXP_LIKE(p_curr, '^|[* 0-9]+|') THEN
    
    
             v_id := REGEXP_SUBSTR(p_curr, '[0-9]+');
             v_row := REGEXP_REPLACE(
                         p_curr, '|',
                         '|' || LPAD(g_map(v_id), GREATEST(g_len-8, 6)) || ' |',
                         1, 2
                         );
          ELSE
             v_row := p_curr;
          END IF;
    
    
          RETURN xplan_ot(v_row);
    
    
       END prepare_row;
    
    
       ----------------------------------------------------------------------------
       FUNCTION display( p_table_name   IN VARCHAR2 DEFAULT 'PLAN_TABLE',
                         p_statement_id IN VARCHAR2 DEFAULT NULL,
                         p_format       IN VARCHAR2 DEFAULT 'TYPICAL' )
          RETURN xplan_ntt PIPELINED IS
    
    
          v_plan_table   VARCHAR2(128) := NVL(p_table_name, 'PLAN_TABLE');
          v_sql          VARCHAR2(512);
          v_binds        ntt_order_map_binds := ntt_order_map_binds();
    
    
       BEGIN
    
          reset_state();
    
    
          -- Prepare the inputs for the order map...
          -- ---------------------------------------
          v_sql := 'SELECT id, parent_id
                    FROM   ' || v_plan_table || '
                    WHERE  plan_id = (SELECT MAX(plan_id)
                                      FROM   ' || v_plan_table || '
                                      WHERE  id = 0 %bind%)
                    ORDER  BY id';
    
    
          IF p_statement_id IS NULL THEN
             v_sql := REPLACE(v_sql, '%bind%');
          ELSE
             v_sql := REPLACE(v_sql, '%bind%', 'AND statement_id = :bv_statement_id');
             v_binds := ntt_order_map_binds(p_statement_id);
          END IF;
    
          -- Build the order map...
          -- --------------------------------------------------
          build_order_map(v_sql, v_binds);
    
    
          -- Now we can call DBMS_XPLAN to output the plan...
          -- ------------------------------------------------
          FOR r_plan IN ( SELECT plan_table_output AS p
                          ,      LEAD(plan_table_output) OVER (ORDER BY ROWNUM) AS np
                          FROM   TABLE(
                                    DBMS_XPLAN.DISPLAY(
                                       v_plan_table, p_statement_id, p_format
                                       ))
                          ORDER  BY
                                 ROWNUM)
          LOOP
             IF g_map.COUNT > 0 THEN
                PIPE ROW (prepare_row(r_plan.p, r_plan.np));
             ELSE
                PIPE ROW (xplan_ot(r_plan.p));
             END IF;
          END LOOP;
    
    
          reset_state();
          RETURN;
    
    
       END display;
    
    
       ----------------------------------------------------------------------------
       FUNCTION display_cursor( p_sql_id          IN VARCHAR2 DEFAULT NULL,
                                p_cursor_child_no IN INTEGER  DEFAULT 0,
                                p_format          IN VARCHAR2 DEFAULT 'TYPICAL' )
          RETURN xplan_ntt PIPELINED IS
    
    
          v_sql_id   v$sql_plan.sql_id%TYPE;
          v_child_no v$sql_plan.child_number%TYPE;
          v_sql      VARCHAR2(256);
          v_binds    ntt_order_map_binds := ntt_order_map_binds();
    
    
       BEGIN
    
    
          reset_state();
    
    
          -- Set a SQL_ID if default parameters passed...
          -- --------------------------------------------
          IF p_sql_id IS NULL THEN
             SELECT prev_sql_id, prev_child_number
             INTO   v_sql_id, v_child_no
             FROM   v$session
             WHERE  sid = (SELECT m.sid FROM v$mystat m WHERE ROWNUM = 1)
             AND    username IS NOT NULL
             AND    prev_hash_value <> 0;
          ELSE
             v_sql_id := p_sql_id;
             v_child_no := p_cursor_child_no;
          END IF;
    
    
          -- Prepare the inputs for the order mapping...
          -- -------------------------------------------
          v_sql := 'SELECT id, parent_id
                    FROM   v$sql_plan
                    WHERE  sql_id = :bv_sql_id
                    AND    child_number = :bv_child_no';
    
    
          v_binds := ntt_order_map_binds(v_sql_id, v_child_no);
    
          -- Build the plan order map from the SQL...
          -- ----------------------------------------
          build_order_map(v_sql, v_binds);
    
    
          -- Now we can call DBMS_XPLAN to output the plan...
          -- ------------------------------------------------
          FOR r_plan IN ( SELECT plan_table_output AS p
                          ,      LEAD(plan_table_output) OVER (ORDER BY ROWNUM) AS np
                          FROM   TABLE(
                                    DBMS_XPLAN.DISPLAY_CURSOR(
                                       v_sql_id, v_child_no, p_format
                                       ))
                          ORDER  BY
                                 ROWNUM)
          LOOP
             IF g_map.COUNT > 0 THEN
                PIPE ROW (prepare_row(r_plan.p, r_plan.np));
             ELSE
                PIPE ROW (xplan_ot(r_plan.p));
             END IF;
          END LOOP;
    
    
          reset_state();
          RETURN;
    
    
       END display_cursor;
    
    
    &_awr_start
       ----------------------------------------------------------------------------
       FUNCTION display_awr( p_sql_id          IN VARCHAR2,
                             p_plan_hash_value IN INTEGER  DEFAULT NULL,
                             p_db_id           IN INTEGER  DEFAULT NULL,
                             p_format          IN VARCHAR2 DEFAULT 'TYPICAL' )
          RETURN xplan_ntt PIPELINED IS
    
          v_sql      VARCHAR2(256);
          v_binds    ntt_order_map_binds := ntt_order_map_binds();
    
    
       BEGIN
    
          reset_state();
    
    
          -- Prepare the SQL for the order mapping...
          -- ----------------------------------------
          v_sql := 'SELECT id, parent_id
                    FROM   dba_hist_sql_plan
                    WHERE  sql_id = :bv_sql_id
                    AND    plan_hash_value = :bv_plan_hash_value
                    AND    dbid = :bv_dbid';
    
    
          -- Determine all plans for the sql_id...
          -- -------------------------------------
          FOR r_awr IN (SELECT DISTINCT
                               sql_id
                        ,      plan_hash_value
                        ,      dbid
                        FROM   dba_hist_sql_plan
                        WHERE  sql_id = p_sql_id
                        AND    plan_hash_value = NVL(p_plan_hash_value, plan_hash_value)
                        AND    dbid = NVL(p_db_id, (SELECT dbid FROM v$database))
                        ORDER  BY
                               plan_hash_value)
          LOOP
    
             -- Prepare the binds and build the order map...
             -- --------------------------------------------
             v_binds := ntt_order_map_binds(r_awr.sql_id,
                                            r_awr.plan_hash_value,
                                            r_awr.dbid);
    
             -- Build the plan order map from the SQL...
             -- ----------------------------------------
             build_order_map(v_sql, v_binds);
    
    
             -- Now we can call DBMS_XPLAN to output the plan...
             -- ------------------------------------------------
             FOR r_plan IN ( SELECT plan_table_output AS p
                             ,      LEAD(plan_table_output) OVER (ORDER BY ROWNUM) AS np
                             FROM   TABLE(
                                       DBMS_XPLAN.DISPLAY_AWR(
                                          r_awr.sql_id, r_awr.plan_hash_value,
                                          r_awr.dbid, p_format
                                          ))
                             ORDER  BY
                                    ROWNUM)
             LOOP
                IF g_map.COUNT > 0 THEN
                   PIPE ROW (prepare_row(r_plan.p, r_plan.np));
                ELSE
                   PIPE ROW (xplan_ot(r_plan.p));
                END IF;
             END LOOP;
    
    
          END LOOP;
    
    
          reset_state();
          RETURN;
    
       END display_awr;
    &_awr_end
    
    
    END xplan;
    /
    
    
    UNDEFINE _awr_start
    UNDEFINE _awr_end
    
    

    效果展示:
    select SQL_ID from v$sqlarea where SQL_TEXT like ‘%select * from scott.emp e,scott.dept d where e.deptno=d.deptno and sal>1000%’;

    SQL_ID

    6fcx62gg7ph2a
    6hw8sn0cb1rm6

    select * from table(xplan.display_cursor(‘6hw8sn0cb1rm6’,0,‘advanced’));

    PLAN_TABLE_OUTPUT
    -------------------------------------
    SQL_ID  6hw8sn0cb1rm6, child number 0
    -------------------------------------
    select * from scott.emp e,scott.dept d where e.deptno=d.deptno and
    sal>1000
    
    Plan hash value: 844388907
    
    ------------------------------------------------------------------------------------------------
    | Id  | Order | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------------------
    |   0 |     6 | SELECT STATEMENT             |         |       |       |     6 (100)|          |
    |   1 |     5 |  MERGE JOIN                  |         |    13 |   754 |     6  (17)| 00:00:01 |
    |   2 |     2 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     4 |    80 |     2   (0)| 00:00:01 |
    |   3 |     1 |    INDEX FULL SCAN           | PK_DEPT |     4 |       |     1   (0)| 00:00:01 |
    |*  4 |     4 |   SORT JOIN                  |         |    13 |   494 |     4  (25)| 00:00:01 |
    |*  5 |     3 |    TABLE ACCESS FULL         | EMP     |    13 |   494 |     3   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------------------
    
    Query Block Name / Object Alias (identified by operation id):
    ---------------------------------------------------------------------
    
       1 - SEL$1
       2 - SEL$1 / D@SEL$1
       3 - SEL$1 / D@SEL$1
       5 - SEL$1 / E@SEL$1
    
    Outline Data
    -------------
    
      /*+
          BEGIN_OUTLINE_DATA
          IGNORE_OPTIM_EMBEDDED_HINTS
          OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
          DB_VERSION('11.2.0.4')
          ALL_ROWS
          OUTLINE_LEAF(@"SEL$1")
          INDEX(@"SEL$1" "D"@"SEL$1" ("DEPT"."DEPTNO"))
          FULL(@"SEL$1" "E"@"SEL$1")
          LEADING(@"SEL$1" "D"@"SEL$1" "E"@"SEL$1")
          USE_MERGE(@"SEL$1" "E"@"SEL$1")
          PX_JOIN_FILTER(@"SEL$1" "E"@"SEL$1")
          END_OUTLINE_DATA
      */
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       4 - access("E"."DEPTNO"="D"."DEPTNO")
           filter("E"."DEPTNO"="D"."DEPTNO")
       5 - filter("SAL">1000)
    
    Column Projection Information (identified by operation id):
    -----------------------------------------------------------
    
       1 - "D"."DEPTNO"[NUMBER,22], "E"."DEPTNO"[NUMBER,22],
           "D"."LOC"[VARCHAR2,13], "D"."DNAME"[VARCHAR2,14], "E"."EMPNO"[NUMBER,22],
           "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9], "E"."MGR"[NUMBER,22],
           "E"."HIREDATE"[DATE,7], "SAL"[NUMBER,22], "E"."COMM"[NUMBER,22]
       2 - "D"."DEPTNO"[NUMBER,22], "D"."DNAME"[VARCHAR2,14], "D"."LOC"[VARCHAR2,13]
       3 - "D".ROWID[ROWID,10], "D"."DEPTNO"[NUMBER,22]
       4 - (#keys=1) "E"."DEPTNO"[NUMBER,22], "E"."EMPNO"[NUMBER,22],
           "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9], "E"."MGR"[NUMBER,22],
           "E"."HIREDATE"[DATE,7], "SAL"[NUMBER,22], "E"."COMM"[NUMBER,22]
       5 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9],
           "E"."MGR"[NUMBER,22], "E"."HIREDATE"[DATE,7], "SAL"[NUMBER,22],
           "E"."COMM"[NUMBER,22], "E"."DEPTNO"[NUMBER,22]
    
    
    67 rows selected.
    
    

    更多Oracle优化资讯:https://www.modb.pro/tag/oracle?cyn  

  • 相关阅读:
    BERT模型fine-tuning代码解析(一)
    使用BERT获取中文词向量
    使用BERT获取中文词向量
    中文情感分类任务如何对bert语言模型微调,微调后的模型如何使用
    Tencent_AILab_ChineseEmbedding使用(×××××)
    中文自然语言处理数据集
    Anaconda环境的创建/激活/删除/管理
    thymeleaf和vue的关系
    模拟测试20191013
    模拟测试20191011-2
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13311747.html
Copyright © 2020-2023  润新知