Salut amis DBA,
Je travaille actuellement sur une table IOT et j'ai un pb : comment Oracle accède-t-il à la table quand on utilise un index secondaire?
Une table IOT est en vrai un index MAIS je peux créer dessus un autre index, dit index secondaire ou index sur index.
Ce type de table a des logical rowid au lieu de physical rowid (pour les tables classiques) et, avec ce logical rowid, Oracle peut recalculer la pk.
Le pb est que lorsque la table est fortement modifiée via des ordres DML, les données bougent dans les blocs (car l'index est trié) ET il n'y a plus de lien entre le logical rowid de l'index secondaire et le bloc où se trouve les données dans l'index primaire. Oracle utilise pour cela une colonne, dans DBA_INDEXES, appelée PCT_DIRECT_ACCESS. Si elle vaut 100, on accède directement depuis l'index secondaire à l'index primaire sinon il faut recalculer la pk depuis le logical rowid de l'index secondaire puis parcourir l'index primaire. Donc, d'après ce que je comprends, Oracle accède aux données de l'index primaire selon deux façon différentes et c'est ça ce que je veux voir.
Et c'est là où ça coince... D'après mes tests, je ne vois pas de différence dans le plan d'exécution dans la façon dont on accède à l'index primaire selon que la colonne PCT_DIRECT_ACCESS vaut 100 ou bien moins.
Voici mes test.
Création de la table.
Nom de l'index correspondant à la table IOT.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 SQL> create table ZZ_IOT (id number primary key, first_name varchar2(50 char), last_name varchar2(50 char), created date) organization index; Table created. SQL> INSERT INTO zz_iot SELECT rownum, 'First_name' || to_char(rownum), 'Last_name' || to_char(rownum), sysdate FROM dual CONNECT BY LEVEL <=1000; 1000 rows created. SQL> commit; Commit complete.
Exemples de logical rowid.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 SQL> select object_name, object_type from user_objects where object_name in ('ZZ_IOT', 'SYS_IOT_TOP_80205') order by object_name; OBJECT_NAME OBJECT_TYPE -------------------- ----------------------- SYS_IOT_TOP_80205 INDEX ZZ_IOT TABLE
La table IOT est bien considérée comme un index, comme le montre le plan d'exécution : on accède aux données via un INDEX UNIQUE SCAN.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 SQL> select rowid from zz_iot where rownum < 11 order by rowid; ROWID ----------------------------------------- *BAMAJEQDwgQ3/g *BAMAJEQDwgQ4/g *BAMAJEQDwgQ5/g *BAMAJEQDwgQ6/g *BAMAJEQDwgQ7/g *BAMAJEQDwgQ8/g *BAMAJEQDwgQ9/g *BAMAJEQDwgQ+/g *BAMAJEQDwgQ//g *BAMAJEQDwgRA/g 10 rows selected. SQL> exec dbms_stats.gather_table_stats('', 'ZZ_IOT'); PL/SQL procedure successfully completed.
Maintenant on crée un index secondaire : un index sur index puisque la table IOT est en réalité un index.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 SQL> select * from zz_iot where id = 200; ID FIRST_NAME LAST_NAME CREATED ---------- -------------------------------------------------- -------------------------------------------------- --------- 200 First_name200 Last_name200 23-DEC-18 SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL ,NULL, 'TYPICAL')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 4u2tuk6g76g5n, child number 0 ------------------------------------- select * from zz_iot where id = 200 Plan hash value: 1555085035 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 1 (100)| | |* 1 | INDEX UNIQUE SCAN| SYS_IOT_TOP_80205 | 1 | 226 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("ID"=200) 18 rows selected.
Que se passe-t-il quand je cherche depuis cet index secondaire? Il y a deux parcours d'index... normal?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 SQL> create index idx_zz_iot_first_name on zz_iot(first_name); Index created. SQL> select index_name, INDEX_TYPE, TABLE_TYPE, UNIQUENESS from dba_indexes where table_name = 'ZZ_IOT' order by 1; INDEX_NAME INDEX_TYPE TABLE_TYPE UNIQUENES ------------------------- --------------------------- ----------- --------- IDX_ZZ_IOT_FIRST_NAME NORMAL TABLE NONUNIQUE SYS_IOT_TOP_80205 IOT - TOP TABLE UNIQUE SQL> exec dbms_stats.gather_table_stats('', 'ZZ_IOT', cascade=>TRUE); PL/SQL procedure successfully completed.
Pourtant j'ai la colonne PCT_DIRECT_ACCESS à 100 donc Oracle devrait pouvoir accéder DIRECTEMENT à la donnée via l'index primaire... oui mais via quelle opération? Index access by rowid? ça n'existe pas.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 SQL> select * from zz_iot where first_name = 'First_name200'; ID FIRST_NAME LAST_NAME CREATED ---------- -------------------------------------------------- ----------------------------- 200 First_name200 Last_name200 23-DEC-18 SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL ,NULL, 'TYPICAL')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 4b5xhv7xbfzm0, child number 0 ------------------------------------- select * from zz_iot where first_name = 'First_name200' Plan hash value: 2156475558 ------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 1 (100)| | |* 1 | INDEX UNIQUE SCAN| SYS_IOT_TOP_80205 | 1 | 39 | 1 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN| IDX_ZZ_IOT_FIRST_NAME | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("FIRST_NAME"='First_name200') 2 - access("FIRST_NAME"='First_name200') 20 rows selected.
Quel est le plan d'exécution si j'utilise un logical rowid dans le WHERE?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 SQL> select INDEX_NAME, INDEX_TYPE, PCT_DIRECT_ACCESS from USER_INDEXES where table_name = 'ZZ_IOT' order by 1; INDEX_NAME INDEX_TYPE PCT_DIRECT_ACCESS ------------------------------ --------------------------------- IDX_ZZ_IOT_FIRST_NAME NORMAL 100 SYS_IOT_TOP_80205 IOT - TOP 0
Oh, intéressant, même si on a bien un prédicat d'accès via le ROWID, Oracle met dans le plan d'exécution une opération de type INDEX UNIQUE SCAN.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 SQL> select rowid from zz_iot where id = 200; ROWID ----------------------------------------- *BAMAJEcCwgP+ SQL> select * from zz_iot where rowid = '*BAMAJEcCwgP+'; ID FIRST_NAME LAST_NAME CREATED ---------- ------------------------------------------------- 200 First_name200 Last_name200 23-DEC-18
Cela veut-il dire qu'Oracle doive toujours parcourir l'index lié à la PK et que son logical rowid ne permette pas un accès direct au bloc de l'index de la pk?
On essaye de mettre le champ PCT_DIRECT_ACCESS à 0 pour voir si cela change le plan d'exécution.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL ,NULL, 'TYPICAL')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID c3r6nb19kc154, child number 0 ------------------------------------- select * from zz_iot where rowid = '*BAMAJEcCwgP+' Plan hash value: 1555085035 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 1 (100)| | |* 1 | INDEX UNIQUE SCAN| SYS_IOT_TOP_80205 | 1 | 39 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access(ROWID=:B1) 18 rows selected.
Pour se faire on va supprimer 90% de la table puis faire un shrink pour forcer le recalcul de rowid dans la table et donc un décalage avec ceux de l'index.
Pas de ENABLE ROW MOVEMENT pour une table IOT.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 SQL> delete from zz_iot where mod(id, 10) <> 0; 900 rows deleted. SQL> commit; Commit complete.
Héhé, on est passé de 100 à 17! Mais le plan d'exécution ne change pas... zut. Faut-il passer à 0?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 SQL> alter table zz_iot ENABLE ROW MOVEMENT; alter table zz_iot ENABLE ROW MOVEMENT * ERROR at line 1: ORA-14066: illegal option for a non-partitioned index-organized table SQL> ALTER TABLE zz_iot SHRINK SPACE; Table altered. SQL> exec dbms_stats.gather_table_stats('', 'ZZ_IOT'); PL/SQL procedure successfully completed. SQL> exec dbms_stats.gather_index_stats('', 'IDX_ZZ_IOT_FIRST_NAME'); PL/SQL procedure successfully completed.
Voilà, c'était long, merci à ceux qui ont tout lu et j'attends avec impatience vos remarques :-)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 SQL> select INDEX_NAME, INDEX_TYPE, PCT_DIRECT_ACCESS from USER_INDEXES where table_name = 'ZZ_IOT' order by 1; INDEX_NAME INDEX_TYPE PCT_DIRECT_ACCESS -------------------- -------------------------------------- IDX_ZZ_IOT_FIRST_NAME NORMAL 17 SYS_IOT_TOP_80205 IOT - TOP 0 SQL> set autotrace on SQL> select * from zz_iot where first_name = 'First_name10'; ID FIRST_NAME LAST_NAME CREATED ---------- -------------------------------------------------- -------------------------------------------------- --------- 10 First_name10 Last_name10 23-DEC-18 Execution Plan ---------------------------------------------------------- Plan hash value: 2156475558 ------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 39 | 2 (0)| 00:00:01 | |* 1 | INDEX UNIQUE SCAN| SYS_IOT_TOP_80205 | 1 | 39 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN| IDX_ZZ_IOT_FIRST_NAME | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("FIRST_NAME"='First_name10') 2 - access("FIRST_NAME"='First_name10')
Partager