您的位置:首页 > 数据库

xplan.sql(本脚本获取执行计划显示执行顺序)

2015-09-06 15:11 381 查看
-- ----------------------------------------------------------------------------------------------
--
-- Script:       xplan.sql
--
-- Author:       Adrian Billington
--
--
-- Description:  Creates a package named XPLAN as a wrapper over DBMS_XPLAN. Provides access to
--               the following DBMS_XPLAN pipelined functions:
--
--                  1\. DISPLAY;
--                  2\. DISPLAY_CURSOR;
--                  3\. DISPLAY_AWR (optional - see Notes section for licence implications).
--
--               The XPLAN wrapper package has one purpose: to include an "order" column in the
--               plan output to show the order in which plan operations are performed. See the
--               following example for details.
--
-- Example:      DBMS_XPLAN output (format BASIC):
--               ------------------------------------------------
--               | Id  | Operation                    | Name    |
--               ------------------------------------------------
--               |   0 | SELECT STATEMENT             |         |
--               |   1 |  MERGE JOIN                  |         |
--               |   2 |   TABLE ACCESS BY INDEX ROWID| DEPT    |
--               |   3 |    INDEX FULL SCAN           | PK_DEPT |
--               |   4 |   SORT JOIN                  |         |
--               |   5 |    TABLE ACCESS FULL         | EMP     |
--               ------------------------------------------------
--
--               Equivalent XPLAN output (format BASIC):
--               --------------------------------------------------------
--               | Id  | Order | Operation                    | Name    |
--               --------------------------------------------------------
--               |   0 |     6 | SELECT STATEMENT             |         |
--               |   1 |     5 |  MERGE JOIN                  |         |
--               |   2 |     2 |   TABLE ACCESS BY INDEX ROWID| DEPT    |
--               |   3 |     1 |    INDEX FULL SCAN           | PK_DEPT |
--               |   4 |     4 |   SORT JOIN                  |         |
--               |   5 |     3 |    TABLE ACCESS FULL         | EMP     |
--               --------------------------------------------------------
--
-- Usage:        SELECT * FROM TABLE(XPLAN.DISPLAY(...));
--               SELECT * FROM TABLE(XPLAN.DISPLAY_CURSOR(...));
--               SELECT * FROM TABLE(XPLAN.DISPLAY_AWR(...));
--
--               Usage for XPLAN is exactly the same as for DBMS_XPLAN. See the DBMS_XPLAN
--               documentation for all options.
--
--               Note that the only exception to this is that XPLAN.DISPLAY does not contain
--               the FILTER_PREDS parameter available in 10.2+ versions of DBMS_XPLAN.DISPLAY
--               (this parameter enables us to limit the data being returned from an Explain
--               Plan but is of quite limited use).
--
--               See the Notes section for details on the licensing implications of using
--               XPLAN.DISPLAY_AWR.
--
-- Versions:     This utility will work for all versions of 10g and upwards.
--
-- Required:     1) PLAN_TABLE of at least 10.1 format
--
--               2) Either:
--                     SELECT ANY DICTIONARY
--                  Or:
--                     SELECT on V$DATABASE
--                     SELECT on V$SQL_PLAN
--                     SELECT on V$SESSION
--                     SELECT on V$MYSTAT
--                     SELECT on DBA_HIST_SQL_PLAN
--
--               3) CREATE TYPE, CREATE PROCEDURE
--
-- Notes:        *** IMPORTANT: PLEASE READ ***
--
--               1) Oracle license implications
--                  ---------------------------
--                  The AWR functionality of XPLAN accesses a DBA_HIST% view which means
--                  that it requires an Oracle Diagnostic Pack license. The XPLAN.DISPLAY_AWR
--                  pipelined function is therefore disabled by default. It can be included
--                  by modifying two substitution variables at the start of the script. Please
--                  ensure that you are licensed to use this feature: the author accepts
--                  no responsibility for any use of this functionality in an unlicensed database.
--
-- Installation: Installation requires SQL*Plus or any IDE that supports substitution
--               variables and SQL*Plus SET commands. To install, simply run the script in
--               the target schema.
--
-- Creates:      1) XPLAN_OT object type
--               2) XPLAN_NTT collection type
--               3) XPLAN package
--
-- Removal:      1) DROP PACKAGE xplan;
--               3) DROP TYPE xplan_ntt;
--               4) DROP TYPE xplan_ot;
--
--
-- ----------------------------------------------------------------------------------------------

--
-- Define the "commenting-out" substitution variables for the AWR elements of this utility. The
-- default is commented out. To include the AWR functionality, change the variables to " " (i.e.
-- a single space).
--

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: