SQL Patch another example

In a post I wrote last year about SQL Patch, I only showed how to use SQL Patch to influence the execution plan of a query with hint injection which disables the existing hardcoded hint. Here I am going to use another example to show how to add a new hint.

With Oracle 19c, create_sql_patch from the package dbms_sqldiag can be used. For the following query, an index full scan is used.


15:06:30 SQL> select count(*) from emp t;

  COUNT(*)
----------
    851010

Elapsed: 00:00:00.07

Execution Plan
----------------------------------------------------------
Plan hash value: 3505053556

--------------------------------------------------------------------------------------
| Id  | Operation        | Name                      | Rows  | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                           |     1 |   135   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE  |                           |     1 |            |          |
|   2 |   INDEX FULL SCAN| IX_HOR_SCHED_CREATED_TIME |   880K|   135   (0)| 00:00:01 |
--------------------------------------------------------------------------------------


Statistics
----------------------------------------------------------
         17  recursive calls
          0  db block gets
      20970  consistent gets
          0  physical reads
       3244  redo size
        552  bytes sent via SQL*Net to client
        402  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

Now I am giving it a full table scan hint. First find the query SQL ID.

Hselect sql_id, hash_value, ELAPSED_TIME,child_number, EXECUTIONS, plan_hash_value, LAST_ACTIVE_TIME, ELAPSED_TIME/EXECUTIONS each 
from v$sql where sql_id in 
(select sql_id from v$sql where sql_fulltext like 'select count(*) from emp%')  
and executions>0 order by LAST_ACTIVE_TIME;

Then check its execution plan:

14:30:27 SQL> SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR('gh6dnmvduw84m', 0, FORMAT=>'OUTLINE BASIC NOTE'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
EXPLAINED SQL STATEMENT:
------------------------
select count(*) from emp t

Plan hash value: 3505053556

------------------------------------------------------
| Id  | Operation        | Name                      |
------------------------------------------------------
|   0 | SELECT STATEMENT |                           |
|   1 |  SORT AGGREGATE  |                           |
|   2 |   INDEX FULL SCAN| IX_HOR_SCHED_CREATED_TIME |
------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      OPT_PARAM('optimizer_index_cost_adj' 1)
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX(@"SEL$1" "T"@"SEL$1" "IX_HOR_SCHED_CREATED_TIME")
      END_OUTLINE_DATA
  */


29 rows selected.

Now, create a SQL Patch with the full table scan hint. The trick here is the hint must include query block names such as @”SEL$1″. For example the output above has

INDEX(@"SEL$1" "T"@"SEL$1" "IX_HOR_SCHED_CREATED_TIME")

Without it, the hint won’t be resolvable by the optimizer. And you will see the SQL Patch is used when checking the executuion plan, but there will be a Hint report section which shows somethig like below.

Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 3 (N - Unresolved (1))
---------------------------------------------------------------------------
   1 -  SEL$E41D89D5
         N -  USE_HASH(tab1 tab2)

Use the PL/SQL block below to create the SQL Patch for “select /*+ FULL(t) */ count(*) from emp t;” As you can see, the hint text is ‘FULL(@”SEL$1″ “T”@”SEL$1”)‘ with @”SEL$1″ attached.

DECLARE
  l_patch_name  VARCHAR2(32767);
BEGIN

  -- SQL ID
  l_patch_name := SYS.DBMS_SQLDIAG.create_sql_patch(
    sql_id    => 'gh6dnmvduw84m',
    hint_text => 'FULL(@"SEL$1" "T"@"SEL$1")',
    name      => 'gh6dnmvduw84m');
END;
/

14:31:32 SQL> select name, CATEGORY, SIGNATURE, CREATED, status , SQL_TEXT from dba_sql_patches where name='gh6dnmvduw84m';

NAME            CATEGORY                     SIGNATURE CREATED                            STATUS   SQL_TEXT
--------------- --------------- ---------------------- ---------------------------------- -------- ----------------------------------------
gh6dnmvduw84m   DEFAULT            3913986059900980868 12-FEB-21 02.31.31.854625 PM       ENABLED  select count(*) from emp t

Elapsed: 00:00:00.01

Now let’s run the query again, index full scan is gone and a full table scan is used with indication “SQL patch “gh6dnmvduw84m” used for this statement” and there is no Hint report section because it succeeds. If for whatever reason there is something wrong with the hint, you will see the Hint report section.

14:31:57 SQL> set autotrace on
14:32:01 SQL> select count(*) from emp t;

  COUNT(*)
----------
    871796

Elapsed: 00:00:26.79

Execution Plan
----------------------------------------------------------
Plan hash value: 2634367177

--------------------------------------------------------------------------------
| Id  | Operation          | Name              | Rows  | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                   |     1 |   364K  (1)| 00:00:15 |
|   1 |  SORT AGGREGATE    |                   |     1 |            |          |
|   2 |   TABLE ACCESS FULL| EMP |   860K|   364K  (1)| 00:00:15 |
--------------------------------------------------------------------------------

Note
-----
   - SQL patch "gh6dnmvduw84m" used for this statement


Statistics
----------------------------------------------------------
         15  recursive calls
          0  db block gets
    1336779  consistent gets
    1336724  physical reads
          0  redo size
        552  bytes sent via SQL*Net to client
        402  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

Checking outline:

14:33:32 SQL> SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR('gh6dnmvduw84m', 0, FORMAT=>'OUTLINE BASIC NOTE'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
EXPLAINED SQL STATEMENT:
------------------------
select count(*) from emp t

Plan hash value: 2634367177

------------------------------------------------
| Id  | Operation          | Name              |
------------------------------------------------
|   0 | SELECT STATEMENT   |                   |
|   1 |  SORT AGGREGATE    |                   |
|   2 |   TABLE ACCESS FULL| EMP |
------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      OPT_PARAM('optimizer_index_cost_adj' 1)
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "T"@"SEL$1")
      END_OUTLINE_DATA
  */

Note
-----
   - SQL patch "gh6dnmvduw84m" used for this statement

Checking with ADVANCED option:

14:34:16 SQL> SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR('gh6dnmvduw84m', 0, 'ADVANCED'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  gh6dnmvduw84m, child number 0
-------------------------------------
select count(*) from emp t

Plan hash value: 2634367177

--------------------------------------------------------------------------------
| Id  | Operation          | Name              | Rows  | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                   |       |   364K(100)|          |
|   1 |  SORT AGGREGATE    |                   |     1 |            |          |
|   2 |   TABLE ACCESS FULL| EMP |   860K|   364K  (1)| 00:00:15 |
--------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1
   2 - SEL$1 / T@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      OPT_PARAM('optimizer_index_cost_adj' 1)
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "T"@"SEL$1")
      END_OUTLINE_DATA
  */

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - (#keys=0) COUNT(*)[22]
   2 - (rowset=1019)

Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------

   2 -  SEL$1 / T@SEL$1
           -  FULL(@"SEL$1" "T"@"SEL$1")

Note
-----
   - SQL patch "gh6dnmvduw84m" used for this statement

Query Block Registry:
---------------------

  <q o="2" f="y"><n><![CDATA[SEL$1]]></n><f><h><t><![CDATA[T]]></t><s><![C
        DATA[SEL$1]]></s></h></f></q>



59 rows selected.

Elapsed: 00:00:00.08

Also we can get the hint text from sqlobj$data

14:54:27 SQL> select EXACT_MATCHING_SIGNATURE from v$sql where sql_id = 'gh6dnmvduw84m';

EXACT_MATCHING_SIGNATURE
------------------------
     3913986059900980868

select cast(extractvalue(value(x), '/hint') as varchar2(60)) as outline_hints
from xmltable('/outline_data/hint'
	passing (select xmltype(comp_data) xml
				from sys.sqlobj$data
				where signature = 3913986059900980868)) x;

OUTLINE_HINTS
------------------------------------------------------------
FULL(@"SEL$1" "T"@"SEL$1")

select comp_data from sqlobj$data where signature = 3913986059900980868;

Drop the SQL Patch:

14:57:14 SQL> exec sys.DBMS_SQLDIAG.DROP_SQL_PATCH(name => 'gh6dnmvduw84m');

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.02

Reference:

One thought on “SQL Patch another example

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s