Category - TUNING

Managing SQL Profiles

SQL profile
A SQL profile is a database object that contains auxiliary statistics specific to a SQL statement.

Conceptually, a SQL profile is to a SQL statement what object-level statistics are to a table or index. SQL profiles are created when a DBA invokes SQL Tuning Advisor.
The advisor compares the optimizer estimate with values obtained by executing fragments of the statement on a data sample,
When significant variances are found, SQL Tuning Advisor bundles corrective actions together in a SQL profile, and then recommends its acceptance.

SQL profiles do not tie the optimizer to a specific plan or subplan.

A SQL profile is a collection of auxiliary statistics on a query, including all tables and columns referenced in the query.

The profile is stored in an internal format in the data dictionary. 

The user interface is the DBA_SQL_PROFILES dictionary view.

A SQL profile contains, among other statistics, a set of cardinality adjustments.

SQL Profiles and Execution Plans 

The SQL profile contains supplemental statistics for the entire statement, not individual plans. The profile does not itself determine a specific plan.

Internally, a SQL profile is implemented using hints that address different types of problems,These hints do not specify any particular plan.
Rather, the hints correct errors in the optimizer estimation algorithm that lead to suboptimal plans. For example, a profile may use the TABLE_STATS hint to set object statistics for tables when the statistics are missing or stale.

Steps  to tune Queries with help of SQL Profiles

When SQL TUNING Advisor executed manually or automatically ,it may recommend to accept SQL PROFILES for particular SQL query.

Below mentioned is steps to get recommendation by SQL TUNING Advisor .

Step 1=> Creating Tuning Tasks for SQL_ID.

SQL> set long 1000000000
Col recommendations for a200
DECLARE
  l_sql_tune_task_id  VARCHAR2(100);
BEGIN
  l_sql_tune_task_id := DBMS_SQLTUNE.create_tuning_task (
                          sql_id      => '4fz1vtn0w8Aak',
                          scope       => DBMS_SQLTUNE.scope_comprehensive,
                          time_limit  => 180,
                          task_name   => 'tuning_task_for_4fz1vtn0w8aak',
                          description => 'Tuning task for statement 4fz1vtn0w8aak');
  DBMS_OUTPUT.put_line('l_sql_tune_task_id: ' || l_sql_tune_task_id);
END;
/
PL/SQL procedure successfully completed.

Step 2=> Executing Tuning Tasks for SQL_ID.

EXEC DBMS_SQLTUNE.execute_tuning_task(task_name => 'tuning_task_for_4fz1vtn0w8aak');

PL/SQL procedure successfully completed.

Step 3=> Get details recommendationa of Tuning Tasks for SQL_ID.

SET LONG 10000000;
SET PAGESIZE 100000000
SET LINESIZE 200
SELECT DBMS_SQLTUNE.report_tuning_task('tuning_task_for_4fz1vtn0w8aak') AS recommendations FROM dual;
SET PAGESIZE 24

PL/SQL procedure successfully completed.

RECOMMENDATIONS
--------------------------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name   : tuning_task_for_4fz1vtn0w8aak
Tuning Task Owner  : SYS
Workload Type	   : Single SQL Statement
Scope		   : COMPREHENSIVE
Time Limit(seconds): 180
Completion Status  : COMPLETED
Started at	   : 01/16/2022 20:53:09
Completed at	   : 01/16/2022 20:53:10

-------------------------------------------------------------------------------
Schema Name: HR
SQL ID	   : 4fz1vtn0w8aak
SQL Text   : select distinct DEPARTMENT_NAME from DEPT join EMPS using(DEP_ID)
	      where DEPT_NAME like '%ing' and SALARY<90000
-------------------------------------------------------------------------------
FINDINGS SECTION (1 finding)
-------------------------------------------------------------------------------
RECOMMENDATIONS
-------------------------------------------------------
1- SQL Profile Finding (see explain plans section below)
--------------------------------------------------------
  A potentially better execution plan was found for this statement.

  Recommendation (estimated benefit: 33.33%)
  ------------------------------------------
  - Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name =>'tuning_task_for_4fz1vtn0w8aak', task_owner => 'SYS', replace =>TRUE);

Step 4=> Accepting SQL_PROFILE recommendationa of Tuning Tasks for SQL_ID.

execute dbms_sqltune.accept_sql_profile(task_name =>'tuning_task_for_4fz1vtn0w8aak', task_owner => 'SYS', replace =>TRUE);
PL/SQL procedure successfully completed.

Step 5=> Checking Detail for SQL_PROFILE

SQL> select name,status,force_matching from dba_sql_profiles  where task_id in ( select task_id from DBA_ADVISOR_TASKS where task_name ='tuning_task_for_4fz1vtn0w8aak');

NAME                                                                 STATUS   FOR
----------------------------------------------------------------------- ---
SYS_SQLPROF_017e63ab69140000                    ENABLED  NO

SQL>

Step 6=> Checking Detail for SQL_PROFILE while running Query again.

SQL> select * from table( dbms_xplan.display_cursor(format=>'rowstats last ') );

Plan hash value: 2473492969

--------------------------------------------------------------------------------
| Id  | Operation		      | Name	    | Starts | E-Rows | A-Rows |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	      | 	    |	   1 |	      |      0 |
|   1 |  HASH UNIQUE		      | 	    |	   1 |	    1 |      0 |
|   2 |   NESTED LOOPS		      | 	    |	   1 |	    1 |      0 |
|   3 |    NESTED LOOPS 	      | 	    |	   1 |	    1 |      1 |
|*  4 |     TABLE ACCESS FULL	      | EMPLOYEES   |	   1 |	    1 |      1 |
|*  5 |     INDEX UNIQUE SCAN	      | DEPT_ID_PK  |	   1 |	    1 |      1 |
|*  6 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS |	   1 |	    1 |      0 |
--------------------------------------------------------------------------------

Note
-----
   - SQL profile SYS_SQLPROF_017e63ab69140000 used for this statement
   - this is an adaptive plan


Step 7=> Dropping SQL_PROFILE.

SQL> begin
DBMS_SQLTUNE.DROP_SQL_PROFILE(name => 'SYS_SQLPROF_017e63ab69140000');
end;
/

PL/SQL procedure successfully completed.


Basic Operations with SQL PROFILES

1=> get list of sql profiles

SQL> SELECT NAME,type, SQL_TEXT, CATEGORY,TASK_ID ,TASK_EXEC_NAME,STATUS,CREATED FROM DBA_SQL_PROFILES;

NAME			       TYPE    SQL_TEXT 				CATEGORY		TASK_ID TASK_EXEC_NAME	     STATUS   CREATED
------------------------------ ------- ---------------------------------------- -------------------- ---------- -------------
SYS_SQLPROF_017ee45b73920000   MANUAL  select distinct DEPARTMENT_NAME from DEP DEFAULT 		    602 EXEC_643	     DISABLED 10-FEB-22 09.28.25.692585 PM
				       ARTMENTS join EMPLOYEES using(DEPARTMENT
SQL> 

2=> Disabling SQL Profile

BEGIN
DBMS_SQLTUNE.ALTER_SQL_PROFILE(
name => 'SYS_SQLPROF_017ee45b73920000',
attribute_name => 'STATUS',
value => 'DISABLED');
END;
/

SQL> SELECT NAME,type, SQL_TEXT, CATEGORY,TASK_ID ,TASK_EXEC_NAME,STATUS,CREATED FROM DBA_SQL_PROFILES;

NAME			       TYPE    SQL_TEXT 				CATEGORY		TASK_ID TASK_EXEC_NAME	     STATUS   CREATED
------------------------------ ------- ---------------------------------------- -------------------- ---------- -------------------
SYS_SQLPROF_017ee45b73920000   MANUAL  select distinct DEPARTMENT_NAME from DEP DEFAULT 		    602 EXEC_643	     DISABLED 10-FEB-22 09.28.25.692585 PM
				       ARTMENTS join EMPLOYEES using(DEPARTMENT
SQL> 

3=> Check what's is in SQL PROFILE


    SELECT CREATED, PROFILE_NAME, SQL_TEXT,
    extractvalue(VALUE(hint), '.') AS hint
    FROM DBMSHSXP_SQL_PROFILE_ATTR h, DBA_SQL_PROFILES p, TABLE(xmlsequence(extract(xmltype(h.comp_data), '/outline_data/hint'))) hint
   WHERE p.name = h.profile_name
SQL> 

CREATED 		       PROFILE_NAME		      SQL_TEXT						 HINT
------------------------------ ------------------------------ -------------------------------------------------- --------------------------------------------------
10-FEB-22 09.28.25.692585 PM   SYS_SQLPROF_017ee45b73920000   select distinct DEPARTMENT_NAME from DEPARTMENTS j OPT_ESTIMATE(@"SEL$58A6D7F6", TABLE, "EMPLOYEES"@"
							      oin EMPLOYEES using(DEPARTMENT			 SEL$1", SCALE_ROWS=0.0511682243)

10-FEB-22 09.28.25.692585 PM   SYS_SQLPROF_017ee45b73920000   select distinct DEPARTMENT_NAME from DEPARTMENTS j OPT_ESTIMATE(@"SEL$58A6D7F6", TABLE, "DEPARTMENTS"
							      oin EMPLOYEES using(DEPARTMENT			 @"SEL$1", SCALE_ROWS=5.185185185)

10-FEB-22 09.28.25.692585 PM   SYS_SQLPROF_017ee45b73920000   select distinct DEPARTMENT_NAME from DEPARTMENTS j OPT_ESTIMATE(@"SEL$2CBA5DDD", TABLE, "EMPLOYEES"@"
							      oin EMPLOYEES using(DEPARTMENT			 SEL$1", SCALE_ROWS=0.0511682243)

10-FEB-22 09.28.25.692585 PM   SYS_SQLPROF_017ee45b73920000   select distinct DEPARTMENT_NAME from DEPARTMENTS j OPT_ESTIMATE(@"SEL$6AE97DF7", TABLE, "DEPARTMENTS"
							      oin EMPLOYEES using(DEPARTMENT			 @"SEL$1", SCALE_ROWS=5.185185185)

10-FEB-22 09.28.25.692585 PM   SYS_SQLPROF_017ee45b73920000   select distinct DEPARTMENT_NAME from DEPARTMENTS j OPTIMIZER_FEATURES_ENABLE(default)
							      oin EMPLOYEES using(DEPARTMENT

SQL> 

OPT_ESTIMATE

In simple cases where few stats missing or Histogram missing and even adaptive query is not used ,Optimizeer may guess wrong estimates for number of Rows returning from
specific operations as per explain plan operation-id as below.Most cases are likley for Join operations.

SQL> select * from table(dbms_xplan.display_cursor(format=>'+alias'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------
SQL_ID	68vk4d2atahad, child number 0
-------------------------------------
select distinct DEPARTMENT_NAME from DEPARTMENTS join EMPLOYEES using(DEPARTMENT_ID) where DEPARTMENT_NAME like '%ing' and SALARY>20000
Plan hash value: 2473492969
---------------------------------------------------------------------------------------------
| Id  | Operation		      | Name	    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	      | 	    |	    |	    |	  5 (100)|	    |
|   1 |  HASH UNIQUE		      | 	    |	  1 |	 23 |	  5  (20)| 00:00:01 |
|   2 |   NESTED LOOPS		      | 	    |	  1 |	 23 |	  4   (0)| 00:00:01 |
|   3 |    NESTED LOOPS 	      | 	    |	  1 |	 23 |	  4   (0)| 00:00:01 |
|*  4 |     TABLE ACCESS FULL	      | EMPLOYEES   |	  1 |	  7 |	  3   (0)| 00:00:01 |
|*  5 |     INDEX UNIQUE SCAN	      | DEPT_ID_PK  |	  1 |	    |	  0   (0)|	    |
|*  6 |    TABLE ACCESS BY INDEX ROWID| DEPARTMENTS |	  1 |	 16 |	  1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$58A6D7F6
   4 - SEL$58A6D7F6 / EMPLOYEES@SEL$1
   5 - SEL$58A6D7F6 / DEPARTMENTS@SEL$1
   6 - SEL$58A6D7F6 / DEPARTMENTS@SEL$1

So SQL Profile provide an approach to optimizer a source in terms of packaged Hints  which is OPT_ESTIMATE.

OPT_ESTIMATE tells optimizer that the selection need to be corrrected upto the estimated value which is mentioned in block as like below,

 /*+ OPT_ESTIMATE( [query block] operation_type identifier adjustment ) */

So the OPT_ESTIMATE hint,inform the query optimizer that some of its estimations are wrong, and by how much (SCALE_ROWS) or what to use instead (ROWS).

Below example tells optimizer to correct estimate by about 0.05 rows for TABLE selection operation

OPT_ESTIMATE(@"SEL$58A6D7F6", TABLE, "EMPLOYEES"@"SEL$1", SCALE_ROWS=0.0511682243)