11g et Correlations Multi-Colonnes sans statistiques étendues

J’ai essayé de reproduire un problème qu’on rencontre avec 10g avec 11g il y a quelques jours. Il s’agit du type du problème que Riyaj décrit dans son post Multi-Column Correlation and Extended Stats in Oracle 11g. Surprise! Je n’ai pas réussi à reproduire le problème et pourtant, juste changer optimizer_features_enable='10.2.0.4' le fait apparaitre de nouveau; c’était comme si Oracle 11g pouvait détecter les colonnes corrélées sans aucune statistique étendue. Comment est-ce possible ?

Il suffit d’un simple exemple pour illustrer ce changement; créez juste une table comme ci-dessous:

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

begin
for i in 1..1000 loop
for j in 1..10 loop
insert into x values (j,j,j);
end loop;
end loop;
end;
/

commit;

exec dbms_stats.gather_table_stats(-
user,-
'X');

Regardez alors le plan d’une requête contenant une clause WHERE avec 2 colonnes corrélées:

explain plan for select c 
from x
where a=1 and b=1;

select * from table(dbms_xplan.display);

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 900 | 7 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| X | 100 | 900 | 7 (0)| 00:00:01 |
--------------------------------------------------------------------------

L’optimiseur estime que je nombre de lignes retournées par la requête est de 100 ce qui revient à dire que les 2 conditions de la clause WHERE de la requête retournent 1000 lignes chacune et ne sont pas corrélées; c’est évidemment faux puisque dans notre table a=b. Alors quel est le changement avec 10g? Si vous créez un index multi-colonnes sur (A,B), l’estimation d’Oracle 11g change:

alter session set events '10053 trace name context level 1';

create index xidx on x(a,b);

explain plan for select /*+ no_index */ c from x where a=1 and b=1;

select * from table(dbms_xplan.display);

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 9000 | 7 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| X | 1000 | 9000 | 7 (0)| 00:00:01 |
--------------------------------------------------------------------------

alter session set events '10053 trace name context off';

Vous pouvez voir dans la trace 10053 que comme dans le cas de l’utilisation de statistiques étendues, il y a un facteur de correction qui corrige l’estimation du nombre de lignes retournées:

Access path analysis for X
***************************************
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for X[X]
ColGroup (#1, Index) XIDX
Col#: 1 2 CorStregth: 10.00
ColGroup Usage:: PredCnt: 2 Matches Full: #1 Partial: Sel: 0.1000
Table: X Alias: X
Card: Original: 10000.000000 Rounded: 1000 Computed: 1000.00 Non Adjusted: 1000.00
Access Path: TableScan
Cost: 7.16 Resp: 7.16 Degree: 0
Cost_io: 7.00 Cost_cpu: 2412429
Resp_io: 7.00 Resp_cpu: 2412429
ColGroup Usage:: PredCnt: 2 Matches Full: #1 Partial: Sel: 0.1000
ColGroup Usage:: PredCnt: 2 Matches Full: #1 Partial: Sel: 0.1000
Access Path: index (AllEqRange)
Index: XIDX
resc_io: 23.00 resc_cpu: 554643
ix_sel: 0.100000 ix_sel_with_filters: 0.100000
Cost: 23.04 Resp: 23.04 Degree: 1
Best:: AccessPath: TableScan
Cost: 7.16 Degree: 1 Resp: 7.16 Card: 1000.00 Bytes: 0

Ce n’est évidemment pas le cas avec 10g:

alter session set optimizer_features_enable='10.2.0.4';

explain plan for select /*+ no_index 10204 */ c from x where a=1 and b=1;

select * from table(dbms_xplan.display);

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 900 | 7 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| X | 100 | 900 | 7 (0)| 00:00:01 |
--------------------------------------------------------------------------

alter session set optimizer_features_enable='11.1.0.7';

Évidemment, chaque changement à son coté obscur:

explain plan for select /*+ no_index */ c from x where a=1 and b=2;

SQL> select * from table(dbms_xplan.display);

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 9000 | 7 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| X | 1000 | 9000 | 7 (0)| 00:00:01 |
--------------------------------------------------------------------------
SQL> select count(*) 
2 from dba_stat_extensions
3 where table_name='X';

COUNT(*)
----------
0

dans le cas qui m’intéressait le changement pour 11g corrige mon problème et sans autre changement que la mise à jour en 11g. Quoiqu’il en soit, voila qui illustre que vos migrations vers 11g pourront également avoir certains effets de bord.