Entre SQL Plan Management et SQL Profile (1/3)

Vous avez déjà pu explorer SQL Plan Management (11.1+ et Enterprise Edition) et Oracle SQL Profile (10.1+ et Tuning Pack) ! Vous trouverez 3 posts sur le sujet illustrer comment ces 2 outils peuvent s’emmêler jusqu’à donner des résultats surprenants a priori. Nous allons procéder comme suit:

  • Partie 1/3 : Schema exemple, exemple de SQL Tuning Set et de SQL Plan Management
  • Partie 2/3 : Créer un SQL Profile
  • Partie 3/3 : Intégrer le plan du SQL Profile dans SQL Plan Management… et plus

Prêt?

1. Exemple

Pour commencer, vous allez créer une table, l’alimenter, créer 2 index et capturer les statistiques associées:

create table T(
a number,
b number,
c number);

begin
for i in 1..10000 loop
insert into t values (i,i,i);
end loop;
end;
/

create index t_i1 on t(a);

create index t_i2 on t(a,b);

exec dbms_stats.gather_table_stats(user, 'T');

Pour illustrer SQL Plan Management et SQL Profile, nous allons utiliser la requête qui suit:

set lines 120
set pages 1000
alter system flush shared_pool;

exec dbms_application_info.set_module(null,null);

exec dbms_application_info.set_module('DEMO','DEMO');

set serveroutput off

select /*+ no_index(t t_i1) no_index(t t_i2) */ count(c)
from t
where a=1;

COUNT(C)
----------
1

select *
from table(
dbms_xplan.display_cursor(
null,
null,
'basic,rows,cost,projection,predicate')
);

PLAN_TABLE_OUTPUT
------------------------
EXPLAINED SQL STATEMENT:
------------------------
select /*+ no_index(t t_i1) no_index(t t_i2) */ count(c) from t
where a=1

Plan hash value: 2966233522

--------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
--------------------------------------------------------
| 0 | SELECT STATEMENT | | | 9 (100)|
| 1 | SORT AGGREGATE | | 1 | |
|* 2 | TABLE ACCESS FULL| T | 1 | 9 (0)|
--------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"=1)

Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT("C")[22]
2 - "C"[NUMBER,22]

exec dbms_application_info.set_module(null,null);

2. Créer un SQL Tuning Set

Pour faciliter l’utilisation du SQL Tuning Advisor et de SQL Plan Management, vous pouvez commencer par créer un SQL Tuning Set (STS) comme ci-dessous:

col sql_text format a50 word wrapped
col sql_id format a13

exec dbms_sqltune.create_sqlset('DEMO_STS');

select sql_id, sql_text
from v$sql
where module='DEMO'
and sql_text like '%count%';

SQL_ID SQL_TEXT
------------- --------------------------------------------------
9buc0ty0uq98v select /*+ no_index(t t_i1) no_index(t t_i2) */ co
unt(c) from t where a=1

DECLARE
cur DBMS_SQLTUNE.SQLSET_CURSOR;
BEGIN
OPEN cur FOR
SELECT VALUE(P)
FROM table(
DBMS_SQLTUNE.SELECT_CURSOR_CACHE(
'sql_id = ''9buc0ty0uq98v''',
NULL, NULL, NULL, NULL, 1, NULL,
'ALL')) P;
DBMS_SQLTUNE.LOAD_SQLSET(sqlset_name => 'DEMO_STS',
populate_cursor => cur,
update_attributes=>'ALL');
END;
/

select sql_id, sql_text
from table(
dbms_sqltune.select_sqlset('DEMO_STS')
);

SQL_ID SQL_TEXT
------------- --------------------------------------------------
9buc0ty0uq98v select /*+ no_index(t t_i1) no_index(t t_i2) */ co
unt(c) from t where a=1

Vous noterez qu’en spécifiant update_attributes=>'ALL', le plan de la requête est stocké dans le SQL Tuning Set; Vous pouvez le vérifier grâce à dbms_xplan comme ci-dessous:

 select * 
from table(
dbms_xplan.display_sqlset(
sqlset_name => 'DEMO_STS',
sql_id => '9buc0ty0uq98v',
format => 'basic,rows,cost,projection,predicate'));

PLAN_TABLE_OUTPUT
-----------------------------
SQL Tuning Set Name: DEMO_STS
SQL Tuning Set Owner: SCOTT
SQL_ID: 9buc0ty0uq98v
SQL Text: select /*+ no_index(t t_i1) no_index(t t_i2) */ count(c) from t
where a=1
-----------------------------

Plan hash value: 2966233522

--------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
--------------------------------------------------------
| 0 | SELECT STATEMENT | | | 9 (100)|
| 1 | SORT AGGREGATE | | 1 | |
| 2 | TABLE ACCESS FULL| T | 1 | 9 (0)|
--------------------------------------------------------

3. Capturer le plan dans SQL Plan Management

Pour tout savoir sur SQL Plan Management, reportez-vous au Chapitre 15 de Oracle 11g Performance Tuning Guide et aussi aux descriptions de DBMS_SPM, dba_sql_management_config et dba_sql_plan_baselines; Vous pouvez capturer le plan de la requête à partir du SQL Tuning Set comme ci-dessous:

var x number;

exec :x:=DBMS_SPM.LOAD_PLANS_FROM_SQLSET (-
sqlset_name=>'DEMO_STS', -
basic_filter=>'sql_id=''9buc0ty0uq98v''');

print x

X
----------
1

col signature format 99999999999999999999
select sql_handle, enabled, accepted, fixed, signature
from dba_sql_plan_baselines;

SQL_HANDLE ENA ACC FIX SIGNATURE
------------------------------ --- --- --- ---------------------
SYS_SQL_a6604c14af92d9a3 YES YES NO 11988665859788954019

Vous pouvez visualiser les plans contenus dans une SQL Plan Management Baseline au moyen de DBMS_XPLAN comme ci-dessous:

select * 
from table(
dbms_xplan.display_sql_plan_baseline(
sql_handle => 'SYS_SQL_a6604c14af92d9a3',
format => 'basic,rows,cost,projection,predicate'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
SQL handle: SYS_SQL_a6604c14af92d9a3
SQL text: select /*+ no_index(t t_i1) no_index(t t_i2) */ count(c) from t
where a=1
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
Plan name: SYS_SQL_PLAN_af92d9a33fdbb376
Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD
--------------------------------------------------------------------------------

Plan hash value: 2966233522

------ --------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
--------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 9 (0)|
| 1 | SORT AGGREGATE | | 1 | |
|* 2 | TABLE ACCESS FULL| T | 1 | 9 (0)|
--------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"=1)

Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT("C")[22]
2 - "C"[NUMBER,22]

4. Quelques remarques…

Avant d’attaquer la 2ème partie, vous noterez quelques points intéressants:

  • D’abord, la signature de la baseline est identique à celle d’un SQL Profile pour lequel fixed=>’NO’:
var sqltext clob

begin
:sqltext:='select /*+ no_index(t t_i1) no_index(t t_i2) */ count(c)
from t
where a=1';
end;
/

col signature format 99999999999999999999

select dbms_sqltune.sqltext_to_signature(:sqltext) signature from dual;

SIGNATURE
---------------------
11988665859788954019
  • Ensuite, si vous exécutez de nouveau la requête, vous pourrez constater (via l’attribut note) que son plan est désormais calculé à partir de la SQL Plan Management Baseline. C’est d’ailleurs sa raison d’être :
PLAN_TABLE_OUTPUT
------------------------
EXPLAINED SQL STATEMENT:
------------------------
select /*+ no_index(t t_i1) no_index(t t_i2) */ count(c) from t
where a=1

Plan hash value: 2966233522

--------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)|
--------------------------------------------------------
| 0 | SELECT STATEMENT | | | 9 (100)|
| 1 | SORT AGGREGATE | | 1 | |
|* 2 | TABLE ACCESS FULL| T | 1 | 9 (0)|
--------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"=1)

Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT("C")[22]
2 - "C"[NUMBER,22]

Note
-----
- SQL plan baseline SYS_SQL_PLAN_af92d9a33fdbb376 used for this statement
  • Vous pouvez voir le contenu de la baseline en interrogeant sys.sqlobj$data:
col signature format 99999999999999999999
set long 10000
set longchunksize 100
select so.signature,
so.plan_id,
decode(so.obj_type, 1,'SQL_PROFILE',
2,'SQL_PLAN_BASELINE',
3,'SQL_PATCH',
null) obj_type,
sod.comp_data
from sys.sqlobj$ so,
sys.sqlobj$data sod
where so.signature=sod.signature
and so.plan_id=sod.plan_id
and so.obj_type=sod.obj_type
and so.obj_type=2;

SIGNATURE PLAN_ID OBJ_TYPE
--------------------- ---------- -----------------
COMP_DATA
--------------------------------------------------------------------------------
11988665859788954019 1071362934 SQL_PLAN_BASELINE
<outline_data>
<hint><![CDATA[IGNORE_OPTIM_EMBEDDED_HINTS]]></hint>
<hint><![CDATA[OPTIMIZER_FEATURES_ENABLE('11.1.0.7')]]></hint>
<hint><![CDATA[DB_VERSION('11.1.0.7')]]></hint>
<hint><![CDATA[ALL_ROWS]]></hint>
<hint><![CDATA[OUTLINE_LEAF(@"SEL$1")]]></hint>
<hint><![CDATA[FULL(@"SEL$1" "T"@"SEL$1")]]></hint>
</outline_data>
  • Enfin, vous pouvez exporter une SQL Plan Management Baseline à des fins plus ou moins honnêtes:
exec dbms_spm.create_stgtab_baseline('SPM_STGTAB');

var x number

exec :x:=dbms_spm.pack_stgtab_baseline (-
table_name => 'SPM_STGTAB')

print x

X
----------
1

set lines 120

select obj_name, sql_handle, signature from spm_stgtab;

OBJ_NAME SQL_HANDLE SIGNATURE
------------------------------ ------------------------------ ---------------------
SYS_SQL_PLAN_af92d9a33fdbb376 SYS_SQL_a6604c14af92d9a3 11988665859788954019

Voilà pour commencer! Dans la 3ème partie, nous verrons comment ajouter les plans que vous avez choisis ou un plan généré par un SQL Profile à une Baseline… Pour l’instant faisons un zoom sur un SQL Profile et n’hésitez pas à laisser vos commentaires.