INDEX CREATION : COMPUTE STATISTICS
Dans les versions antérieurs à Oracle 10g, on pouvait utiliser la clause COMPUTE STATISTICS dans une commande de CREATE INDEX.
Cette clause a été dépréciée. Elle est toujours supportée pour la compatibilité ascendante, et ne causera pas d'erreur.
Oracle collecte désormais les statistiques automatiquement durant la création ou le rebuild de l'index.
lundi 5 décembre 2011
STATISTICS : NO_INVALIDATE
Le calcul de statistiques via DBMS_STATS n'a pas, par défaut, d'impact immédiat sur les plans d'exécution. Ceci est un choix délibéré d'Oracle pour éviter de démultiplier les parsing nécessaires à l'exécution des requêtes. Sauf qu'en contre-partie, on risque d'avoir des surprises.
Par exemple : si j'ai une table vide qui possède des statistiques à jour, et que je lance une requête qui utilise cette table, puis que je charge cette table, que je recalcule les statistiques, et que je relance la requête dans laquelle cette table est impliquée (jointure par ex.). Surprise : le temps d'exécution est extrêmement long (bien trop long), le plan d'exécution n'a pas changé !!
Oracle utilise par défaut la fonction DBMS_STATS.AUTO_INVALIDATE
Aussi, pour forcer l'invalidation des plans d'exécution, Oracle nous laisse la possibilité d'indiquer NO_INVALIDATE => FALSE.
En conclusion, voici ce que je retiens de ce problème :Lorsque l'on charge des tables en annule et remplace (surtout si le nombre de lignes chargé est très variable), il faut tout de suite après le chargement lancer un calcul de statistiques, et avec l'option NO_INVALIDATE => FALSE.
Par exemple : si j'ai une table vide qui possède des statistiques à jour, et que je lance une requête qui utilise cette table, puis que je charge cette table, que je recalcule les statistiques, et que je relance la requête dans laquelle cette table est impliquée (jointure par ex.). Surprise : le temps d'exécution est extrêmement long (bien trop long), le plan d'exécution n'a pas changé !!
Oracle utilise par défaut la fonction DBMS_STATS.AUTO_INVALIDATE
,
fonction qui indique à Oracle d'invalider les plans d'exécution liés à la table en question, lorsque cela lui convient (dans un maximum de 5H de temps), ce afin d'étaler la charge de recalcule des plans d'exécution.Aussi, pour forcer l'invalidation des plans d'exécution, Oracle nous laisse la possibilité d'indiquer NO_INVALIDATE => FALSE.
En conclusion, voici ce que je retiens de ce problème :Lorsque l'on charge des tables en annule et remplace (surtout si le nombre de lignes chargé est très variable), il faut tout de suite après le chargement lancer un calcul de statistiques, et avec l'option NO_INVALIDATE => FALSE.
mercredi 3 août 2011
STATISTIQUES : effet du truncate
Une question que je me pose souvent : le TRUNCATE invalide-t'il les statistiques d'une table ? Réponse : non.
Démonstration :
SQL> CREATE TABLE toto (a number);
Table created.
SQL> INSERT INTO toto VALUES (999);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
BLOCKS AVG_ROW_LEN
---------- -----------
SQL> exec dbms_stats.gather_table_stats(USER,'TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
BLOCKS AVG_ROW_LEN
---------- -----------
5 4
SQL> TRUNCATE TABLE toto;
Table truncated.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
SQL> exec dbms_stats.gather_table_stats(USER,'TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
BLOCKS AVG_ROW_LEN
---------- -----------
0 0
Table created.
SQL> INSERT INTO toto VALUES (999);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
BLOCKS AVG_ROW_LEN
---------- -----------
SQL> exec dbms_stats.gather_table_stats(USER,'TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
BLOCKS AVG_ROW_LEN
---------- -----------
5 4
SQL> TRUNCATE TABLE toto;
Table truncated.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
BLOCKS AVG_ROW_LEN
---------- -----------
5 4
SQL> exec dbms_stats.gather_table_stats(USER,'TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT blocks, avg_row_len FROM USER_TAB_STATISTICS WHERE TABLE_NAME='TOTO';
BLOCKS AVG_ROW_LEN
---------- -----------
0 0
UPDATE : critère supplémentaire dans la table source
On souhaite mettre à jour une table CIB à partir d'une table SRC sur un critère de jointure conditionné par un prédicat supplémentaire.
La difficulté est due au fait que la table à mettre à jour contient des lignes qu'il ne faudra pas mettre à jour et que ce critère est indiqué dans la table source (src.c='NON')
Préparation :
SQL> CREATE TABLE src (a NUMBER, b NUMBER, c CHAR(3));
Table created.
SQL> CREATE TABLE cib (a NUMBER, b NUMBER);
Table created.
SQL> INSERT INTO src VALUES (1,11,'OUI');
1 row created.
SQL> INSERT INTO src VALUES (1,12,'NON');
1 row created.
SQL> INSERT INTO src VALUES (2,22,'NON');
1 row created.
SQL> INSERT INTO cib VALUES (1,0);
1 row created.
SQL> INSERT INTO cib VALUES (2,0);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM src;
A B C
---------- ---------- ---
1 11 OUI
1 12 NON
2 22 NON
SQL> SELECT * FROM cib;
A B
---------- ----------
1 0
2 0
Table created.
SQL> CREATE TABLE cib (a NUMBER, b NUMBER);
Table created.
SQL> INSERT INTO src VALUES (1,11,'OUI');
1 row created.
SQL> INSERT INTO src VALUES (1,12,'NON');
1 row created.
SQL> INSERT INTO src VALUES (2,22,'NON');
1 row created.
SQL> INSERT INTO cib VALUES (1,0);
1 row created.
SQL> INSERT INTO cib VALUES (2,0);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM src;
A B C
---------- ---------- ---
1 11 OUI
1 12 NON
2 22 NON
SQL> SELECT * FROM cib;
A B
---------- ----------
1 0
2 0
Exécution :
Rappel : on souhaite mettre à jour la table cib à partir des valeurs de la table src uniquement lorsque la cle de cib est présente dans src et que le champ c de src vaut 'OUI'
SQL> UPDATE cib
2 SET cib.b = (
3 SELECT src.b
4 FROM src
5 WHERE src.a=cib.a
6 )
7 ;
SELECT src.b
*
ERROR at line 3:
ORA-01427: single-row subquery returns more than one row
La table source renvoie plus d'une valeur pour une valeur de clé donnée
2 SET cib.b = (
3 SELECT src.b
4 FROM src
5 WHERE src.a=cib.a
6 )
7 ;
SELECT src.b
*
ERROR at line 3:
ORA-01427: single-row subquery returns more than one row
La table source renvoie plus d'une valeur pour une valeur de clé donnée
SQL> UPDATE cib
2 SET cib.b = (
3 SELECT src.b
4 FROM src
5 WHERE src.a=cib.a
6 )
7 WHERE src.c='OUI'
8 ;
WHERE src.c='OUI'
*
ERROR at line 7:
ORA-00904: "SRC"."C": invalid identifier
2 SET cib.b = (
3 SELECT src.b
4 FROM src
5 WHERE src.a=cib.a
6 )
7 WHERE src.c='OUI'
8 ;
WHERE src.c='OUI'
*
ERROR at line 7:
ORA-00904: "SRC"."C": invalid identifier
Le prédicat est mal placé
SQL> UPDATE cib
2 SET cib.b = (
3 SELECT src.b
4 FROM src
5 WHERE src.a=cib.a
6 AND src.c='OUI')
7 ;
2 rows updated.
SQL> SELECT * FROM cib;
A B
---------- ----------
1 11
2
Les valeurs des clés sans correspondance sont mises à jour par null
SQL> ROLLBACK;
Rollback complete.
SQL> UPDATE cib
2 SET cib.b = (
3 SELECT src.b
4 FROM src
5 WHERE src.a=cib.a
6 AND src.c='OUI')
7 WHERE EXISTS (
8 SELECT 1
9 FROM src
10 WHERE src.a=cib.a)
11 )
12 ;
2 rows updated.
SQL> SELECT * FROM cib;
A B
---------- ----------
1 11
2
La condition where exists est inefficace, des valeurs des clés sans correspondance sont mises à jour par null
SQL> ROLLBACK;
Rollback complete.
SQL> UPDATE cib
2 SET cib.b = (
3 SELECT src.b
4 FROM src
5 WHERE src.a=cib.a
6 AND src.c='OUI')
7 WHERE EXISTS (
8 SELECT 1
9 FROM src
10 WHERE src.a=cib.a
11 AND src.c='OUI'
12 )
13 ;
1 row updated.
SQL> SELECT * FROM cib;
A B
---------- ----------
1 11
2 0
Écriture correcte, on a le résultat souhaité
SQL> ROLLBACK;
Rollback complete.
Écriture équivalente sous forme de MERGE
SQL> MERGE INTO cib t_c
2 USING (
3 SELECT *
4 FROM src
5 WHERE c='OUI'
6 ) t_s
7 ON (t_c.a=t_s.a)
8 WHEN MATCHED THEN
9 UPDATE SET t_c.b=t_s.b
10 ;
1 row merged.
SQL> SELECT * FROM cib;
A B
---------- ----------
1 11
2 0
SQL> ROLLBACK;
Rollback complete.
UPDATE : vue de jointure in-line
Update de vue de jointure "in-line" et index unique
SQL> create table SRC (a number, b number);
Table created.
SQL> create table CIB (a number, b number);
Table created.
SQL> insert into SRC values (1,11);
1 row created.
SQL> insert into SRC values (2,12);
1 row created.
SQL> insert into CIB values (1,0);
1 row created.
SQL> insert into CIB values (2,0);
1 row created.
SQL> select * from SRC;
A B
---------- ----------
1 11
2 12
SQL> select * from CIB;
A B
---------- ----------
1 0
2 0
SQL> update (
2 select SRC.b srcb, CIB.b cibb
3 from SRC, CIB
4 where SRC.a=CIB.a
5 )
6 set cibb = srcb
7 ;
set cibb = srcb
*
ERROR at line 6:
Table created.
SQL> create table CIB (a number, b number);
Table created.
SQL> insert into SRC values (1,11);
1 row created.
SQL> insert into SRC values (2,12);
1 row created.
SQL> insert into CIB values (1,0);
1 row created.
SQL> insert into CIB values (2,0);
1 row created.
SQL> select * from SRC;
A B
---------- ----------
1 11
2 12
SQL> select * from CIB;
A B
---------- ----------
1 0
2 0
SQL> update (
2 select SRC.b srcb, CIB.b cibb
3 from SRC, CIB
4 where SRC.a=CIB.a
5 )
6 set cibb = srcb
7 ;
set cibb = srcb
*
ERROR at line 6:
ORA-01779: cannot modify a column which maps to a non key-preserved table
Un index unique doit être créé sur la clé de la table source
SQL> create unique index IU_SRC on SRC (a);
Index created.
SQL> update (
2 select SRC.b srcb, CIB.b cibb
3 from SRC, CIB
4 where SRC.a=CIB.a
5 )
6 set cibb = srcb
7 ;
2 rows updated.
SQL> select * from SRC;
A B
---------- ----------
1 11
2 12
SQL> select * from CIB;
A B
---------- ----------
1 11
2 12
Index created.
SQL> update (
2 select SRC.b srcb, CIB.b cibb
3 from SRC, CIB
4 where SRC.a=CIB.a
5 )
6 set cibb = srcb
7 ;
2 rows updated.
SQL> select * from SRC;
A B
---------- ----------
1 11
2 12
SQL> select * from CIB;
A B
---------- ----------
1 11
2 12
mardi 2 août 2011
MERGE : unicité des clés de la table source
Dans un MERGE, la table appelée en source du MERGE (using) ne doit pas avoir de doublons sur sa clé de jointure.
SQL> CREATE TABLE nwprices (
2 article_name VARCHAR2(20),
3 price NUMBER,
4 last_price CHAR(1)
5 );
Table created.
SQL> INSERT INTO articles
2 VALUES ('CABLE 13mm CUIVRE',15.50);
1 row created.
SQL> INSERT INTO articles
2 VALUES ('CABLE 25mm CUIVRE',19.25);
1 row created.
SQL> INSERT INTO nwprices
2 VALUES ('CABLE 13mm CUIVRE',15.80,'O');
1 row created.
SQL> INSERT INTO nwprices
2 VALUES ('CABLE 25mm CUIVRE',21.05,'N');
1 row created.
SQL> INSERT INTO nwprices
2 VALUES ('CABLE 25mm CUIVRE',21.45,'O');
1 row created.
SQL> COL price FOR 99.00
SQL> SELECT * FROM articles;
ARTICLE_NAME PRICE
-------------------- ------
CABLE 13mm CUIVRE 15.50
CABLE 25mm CUIVRE 19.25
SQL> SELECT * FROM nwprices;
ARTICLE_NAME PRICE L
-------------------- ------ -
CABLE 13mm CUIVRE 15.80 O
CABLE 25mm CUIVRE 21.05 N
CABLE 25mm CUIVRE 21.45 O
SQL> MERGE INTO articles ar
2 USING nwprices nw
3 ON (ar.article_name=nw.article_name)
4 WHEN MATCHED
5 THEN UPDATE
6 SET ar.price=nw.price
7 ;
USING nwprices nw
*
ERROR at line 2:
ORA-30926: unable to get a stable set of rows in the source tables
SQL> MERGE INTO articles ar
2 USING (
3 SELECT * FROM nwprices
4 WHERE last_price='O') nw
5 ON (ar.article_name=nw.article_name)
6 WHEN MATCHED
7 THEN UPDATE
8 SET ar.price=nw.price
9 ;
2 rows merged.
SQL> SELECT * FROM articles;
ARTICLE_NAME PRICE
-------------------- ------
CABLE 13mm CUIVRE 15.80
CABLE 25mm CUIVRE 21.45
SQL>
2 article_name VARCHAR2(20),
3 price NUMBER,
4 last_price CHAR(1)
5 );
Table created.
SQL> INSERT INTO articles
2 VALUES ('CABLE 13mm CUIVRE',15.50);
1 row created.
SQL> INSERT INTO articles
2 VALUES ('CABLE 25mm CUIVRE',19.25);
1 row created.
SQL> INSERT INTO nwprices
2 VALUES ('CABLE 13mm CUIVRE',15.80,'O');
1 row created.
SQL> INSERT INTO nwprices
2 VALUES ('CABLE 25mm CUIVRE',21.05,'N');
1 row created.
SQL> INSERT INTO nwprices
2 VALUES ('CABLE 25mm CUIVRE',21.45,'O');
1 row created.
SQL> COL price FOR 99.00
SQL> SELECT * FROM articles;
ARTICLE_NAME PRICE
-------------------- ------
CABLE 13mm CUIVRE 15.50
CABLE 25mm CUIVRE 19.25
SQL> SELECT * FROM nwprices;
ARTICLE_NAME PRICE L
-------------------- ------ -
CABLE 13mm CUIVRE 15.80 O
CABLE 25mm CUIVRE 21.05 N
CABLE 25mm CUIVRE 21.45 O
SQL> MERGE INTO articles ar
2 USING nwprices nw
3 ON (ar.article_name=nw.article_name)
4 WHEN MATCHED
5 THEN UPDATE
6 SET ar.price=nw.price
7 ;
USING nwprices nw
*
ERROR at line 2:
ORA-30926: unable to get a stable set of rows in the source tables
SQL> MERGE INTO articles ar
2 USING (
3 SELECT * FROM nwprices
4 WHERE last_price='O') nw
5 ON (ar.article_name=nw.article_name)
6 WHEN MATCHED
7 THEN UPDATE
8 SET ar.price=nw.price
9 ;
2 rows merged.
SQL> SELECT * FROM articles;
ARTICLE_NAME PRICE
-------------------- ------
CABLE 13mm CUIVRE 15.80
CABLE 25mm CUIVRE 21.45
SQL>
OUTER JOIN : syntaxe ANSI et syntaxe Oracle
Ci-dessous un aide-mémoire pour se souvenir de l'écriture des jointures externes avec les deux syntaxes autorisées dans Oracle.
SQL> CREATE TABLE pays (id_pays NUMBER, pays VARCHAR2(10));
Table created.
SQL> CREATE TABLE villes (
2 id_ville NUMBER,
3 ville VARCHAR2(10),
4 id_pays NUMBER);
Table created.
SQL> INSERT INTO pays VALUES (1, 'USA');
1 row created.
SQL> INSERT INTO pays VALUES (2, 'RUSSIE');
1 row created.
SQL> INSERT INTO pays VALUES (3, 'FRANCE');
1 row created.
SQL> INSERT INTO villes VALUES (1, 'DENVER', 1);
1 row created.
SQL> INSERT INTO villes VALUES (2, 'CHICAGO', 1);
1 row created.
SQL> INSERT INTO villes VALUES (3, 'MOSCOU', 2);
1 row created.
SQL> INSERT INTO villes VALUES (4, 'DJAKARTA', 5);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> -- LEFT OUTER JOIN
SQL> SELECT pays, ville
2 FROM pays p LEFT OUTER JOIN villes v
3 ON p.id_pays=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
SQL> SELECT pays, ville
2 FROM pays p, villes v
3 WHERE p.id_pays=v.id_pays(+)
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
SQL> -- RIGHT OUTER JOIN
SQL> SELECT pays, ville
2 FROM pays p RIGHT OUTER JOIN villes v
3 ON p.id_pays=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
SQL> SELECT pays, ville
2 FROM pays p, villes v
3 WHERE p.id_pays(+)=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
SQL> -- FULL OUTER JOIN
SQL> SELECT pays, ville
2 FROM pays p FULL OUTER JOIN villes v
3 ON p.id_pays=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
SQL> SELECT pays, ville
2 FROM pays p, villes v
3 WHERE p.id_pays=v.id_pays(+)
4 UNION
5 SELECT pays, ville
6 FROM pays p, villes v
7 WHERE p.id_pays(+)=v.id_pays
8 ORDER BY 1,2
9 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
Table created.
SQL> CREATE TABLE villes (
2 id_ville NUMBER,
3 ville VARCHAR2(10),
4 id_pays NUMBER);
Table created.
SQL> INSERT INTO pays VALUES (1, 'USA');
1 row created.
SQL> INSERT INTO pays VALUES (2, 'RUSSIE');
1 row created.
SQL> INSERT INTO pays VALUES (3, 'FRANCE');
1 row created.
SQL> INSERT INTO villes VALUES (1, 'DENVER', 1);
1 row created.
SQL> INSERT INTO villes VALUES (2, 'CHICAGO', 1);
1 row created.
SQL> INSERT INTO villes VALUES (3, 'MOSCOU', 2);
1 row created.
SQL> INSERT INTO villes VALUES (4, 'DJAKARTA', 5);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> -- LEFT OUTER JOIN
SQL> SELECT pays, ville
2 FROM pays p LEFT OUTER JOIN villes v
3 ON p.id_pays=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
SQL> SELECT pays, ville
2 FROM pays p, villes v
3 WHERE p.id_pays=v.id_pays(+)
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
SQL> -- RIGHT OUTER JOIN
SQL> SELECT pays, ville
2 FROM pays p RIGHT OUTER JOIN villes v
3 ON p.id_pays=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
SQL> SELECT pays, ville
2 FROM pays p, villes v
3 WHERE p.id_pays(+)=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
SQL> -- FULL OUTER JOIN
SQL> SELECT pays, ville
2 FROM pays p FULL OUTER JOIN villes v
3 ON p.id_pays=v.id_pays
4 ORDER BY 1,2
5 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
SQL> SELECT pays, ville
2 FROM pays p, villes v
3 WHERE p.id_pays=v.id_pays(+)
4 UNION
5 SELECT pays, ville
6 FROM pays p, villes v
7 WHERE p.id_pays(+)=v.id_pays
8 ORDER BY 1,2
9 ;
PAYS VILLE
---------- ----------
FRANCE
RUSSIE MOSCOU
USA CHICAGO
USA DENVER
DJAKARTA
BIND VARIABLE PEEKING : fonctionnement
Très bon article sur le peeking :
To peek or not to peek
Un autre excellent lien sur le bind variable peeking :
Flushing a cursor out of the Library Cache
Extrait de la doc Oracle 10gR2 :
From Chapter 13 of the Performance tuning Guide:
“13.4.1.2 Peeking of User-Defined Bind Variables
The query optimizer peeks at the values of user-defined bind variables on the first invocation of a cursor. This feature lets the optimizer determine the selectivity of any WHERE clause condition, as well as if literals have been used instead of bind variables. On subsequent invocations of the cursor, no peeking takes place, and the cursor is shared, based on the standard cursor-sharing criteria, even if subsequent invocations use different bind values.
When bind variables are used in a statement, it is assumed that cursor sharing is intended and that different invocations are supposed to use the same execution plan. If different invocations of the cursor would significantly benefit from different execution plans, then bind variables may have been used inappropriately in the SQL statement. Bind peeking works for a specific set of clients, not all clients.”
Oracle 11g :
To peek or not to peek
Un autre excellent lien sur le bind variable peeking :
Flushing a cursor out of the Library Cache
Extrait de la doc Oracle 10gR2 :
From Chapter 13 of the Performance tuning Guide:
“13.4.1.2 Peeking of User-Defined Bind Variables
The query optimizer peeks at the values of user-defined bind variables on the first invocation of a cursor. This feature lets the optimizer determine the selectivity of any WHERE clause condition, as well as if literals have been used instead of bind variables. On subsequent invocations of the cursor, no peeking takes place, and the cursor is shared, based on the standard cursor-sharing criteria, even if subsequent invocations use different bind values.
When bind variables are used in a statement, it is assumed that cursor sharing is intended and that different invocations are supposed to use the same execution plan. If different invocations of the cursor would significantly benefit from different execution plans, then bind variables may have been used inappropriately in the SQL statement. Bind peeking works for a specific set of clients, not all clients.”
Oracle 11g :
Solution définitive aux problèmes de bind varable peeking en 11g avec "Adaptive Cursor Sharing"
Adaptive Cursor Sharing : explication
Adaptive Cursor Sharing : explication
STATISTIQUES : pending statistics 11g
Pending Statistics
Dans les versions d'Oracle antérieures à la 11g, les nouvelles statistiques de l'optimiseur étaient mises à disposition tout de suite. En 11g, c'est toujours le comportement par défaut, mais on a maintentant la possibilité de conserver les statistiques nouvellement générées dans un état d'attente jusqu'à ce que l'on choisisse de les publier.
Par défaut Les statistiques sont automatiquement publiées :
Dans les versions d'Oracle antérieures à la 11g, les nouvelles statistiques de l'optimiseur étaient mises à disposition tout de suite. En 11g, c'est toujours le comportement par défaut, mais on a maintentant la possibilité de conserver les statistiques nouvellement générées dans un état d'attente jusqu'à ce que l'on choisisse de les publier.
Par défaut Les statistiques sont automatiquement publiées :
SQL> SELECT DBMS_STATS.get_prefs('PUBLISH') FROM dual;
DBMS_STATS.GET_PREFS('PUBLISH')
-------------------------------------------
TRUE
1 row selected.
Pour modifier le comportement par défaut, et ne pas publier automatiquement les futures mises à jour
des statistiques de la table SCOTT.EMP :
EXEC DBMS_STATS.set_table_prefs('SCOTT','EMP','PUBLISH','false');
Pour publier automatiquement les futures mises à jour des statistiques de la table SCOTT.EMP :
Pour publier automatiquement les futures mises à jour des statistiques de la table SCOTT.EMP :
EXEC DBMS_STATS.set_table_prefs('SCOTT','EMP','PUBLISH','true');
Vues listant les statistiques à publier :
Vues listant les statistiques à publier :
SELECT * FROM [DBA|ALL|USER]_TAB_PENDING_STATS;
SELECT * FROM [DBA|ALL|USER]_IND_PENDING_STATS;
Pour publier toutes les statistiques en attente :
SELECT * FROM [DBA|ALL|USER]_IND_PENDING_STATS;
Pour publier toutes les statistiques en attente :
EXEC DBMS_STATS.publish_pending_stats(NULL, NULL);
Pour publier les statistiques en attente de l'objet SCOTT.EMP :
Pour publier les statistiques en attente de l'objet SCOTT.EMP :
EXEC DBMS_STATS.publish_pending_stats('SCOTT','EMP');
Pour supprimer les statistiques en attente de l'objet SCOTT.EMP :
Pour supprimer les statistiques en attente de l'objet SCOTT.EMP :
EXEC DBMS_STATS.delete_pending_stats('SCOTT','EMP');
L'optimiseur peut utiliser les statistiques en attente (pending) en positionnant le paramètre OPTIMIZER_USE_PENDING_STATISTICS à TRUE (FALSE par défaut), ce qui revient à supprimer la fonctionnalité de statistiques en atente.
Il est possible également de modifier le paramètre au niveau session, ce qui permet de tester le bénéfice des nouvelles statistiques dans une session avant de les publier.
L'optimiseur peut utiliser les statistiques en attente (pending) en positionnant le paramètre OPTIMIZER_USE_PENDING_STATISTICS à TRUE (FALSE par défaut), ce qui revient à supprimer la fonctionnalité de statistiques en atente.
Il est possible également de modifier le paramètre au niveau session, ce qui permet de tester le bénéfice des nouvelles statistiques dans une session avant de les publier.
ALTER SESSION SET OPTIMIZER_USE_PENDING_STATISTICS=TRUE;
Il est possible de transférer les statistiques en attente d'une base à une autre avec la procédure DBMS_STATS.EXPORT_PENDING_STATS
Il est possible de transférer les statistiques en attente d'une base à une autre avec la procédure DBMS_STATS.EXPORT_PENDING_STATS
lundi 1 août 2011
STATISTIQUES : extended statistics 11g
Statistiques étendues
Oracle peut désormais en 11g, recueillir de nouveaux types de statistiques, rendant la qualité de l'estimation de sélectivité bien meilleure dans les cas concernés.
Statistiques multi-colonnes
Lorsque dans la clause where d'une requête, figurent plusieurs prédicats mono-colonnes d'une même table, la corrélation entre ces colonne peut avoir des conséquences sur la sélectivité de ce groupe de colonnes.
Il est par conséquent très utile de pouvoir définir des groupes de colonnes sur lesquels on déterminera des statistiques de répartition de données afin de rendre plus optimal le choix de l'optimiseur dans ces cas.
Statistiques d'expressions
Lorsque une fonction est appliquée à une colonne dans la clause "where" (fonction(col1)=constant), l'optimiseur n'a aucun moyen de savoir comment cette fonction affecte la sélectivité de la colonne.
En recueillant les statistiques de cette expression, l'optimiseur obtient une valeur de sélectivité bien plus juste.
Oracle peut désormais en 11g, recueillir de nouveaux types de statistiques, rendant la qualité de l'estimation de sélectivité bien meilleure dans les cas concernés.
Statistiques multi-colonnes
Lorsque dans la clause where d'une requête, figurent plusieurs prédicats mono-colonnes d'une même table, la corrélation entre ces colonne peut avoir des conséquences sur la sélectivité de ce groupe de colonnes.
Il est par conséquent très utile de pouvoir définir des groupes de colonnes sur lesquels on déterminera des statistiques de répartition de données afin de rendre plus optimal le choix de l'optimiseur dans ces cas.
Creating a Column Group
DECLARE
cg_name varchar2(30);
BEGIN
cg_name := dbms_stats.create_extended_stats(null,'customers',
'(cust_state_province,country_id)');
END;
/
Getting a Column Group
select sys.dbms_stats.show_extended_stats_name('sh','customers',
'(cust_state_province,country_id)') col_group_name
from dual;
The output is similar to the following:
COL_GROUP_NAME
----------------
SYS_STU#S#WF25Z#QAHIHE#MOFFMM
Dropping a Column Group
exec dbms_stats.drop_extended_stats('sh','customers',
DECLARE
cg_name varchar2(30);
BEGIN
cg_name := dbms_stats.create_extended_stats(null,'customers',
'(cust_state_province,country_id)');
END;
/
Getting a Column Group
select sys.dbms_stats.show_extended_stats_name('sh','customers',
'(cust_state_province,country_id)') col_group_name
from dual;
The output is similar to the following:
COL_GROUP_NAME
----------------
SYS_STU#S#WF25Z#QAHIHE#MOFFMM
Dropping a Column Group
exec dbms_stats.drop_extended_stats('sh','customers',
'(cust_state_province,country_id)');
Monitoring Column Groups
Use the dictionary table user_stat_extensions to obtain information about MultiColumn statistics:
Select extension_name, extension
from user_stat_extensions
where table_name='CUSTOMERS';
EXTENSION_NAME EXTENSION
-----------------------------------------------------------------
SYS_STU#S#WF25Z#QAHIHE#MOFFMM_("CUST_STATE_PROVINCE","COUNTRY_ID")
Use the following query to find the number of distinct values and find whether a histogram has been created for a column group:
select e.extension col_group, t.num_distinct, t.histogram
2 from user_stat_extensions e, user_tab_col_statistics t
3 where e.extension_name=t.column_name
4 and e.table_name=t.table_name
5 and t.table_name='CUSTOMERS';
COL_GROUP NUM_DISTINCT HISTOGRAM
-----------------------------------------------------------------
("COUNTRY_ID","CUST_STATE_PROVINCE") 145 FREQUENCY
Monitoring Column Groups
Use the dictionary table user_stat_extensions to obtain information about MultiColumn statistics:
Select extension_name, extension
from user_stat_extensions
where table_name='CUSTOMERS';
EXTENSION_NAME EXTENSION
-----------------------------------------------------------------
SYS_STU#S#WF25Z#QAHIHE#MOFFMM_("CUST_STATE_PROVINCE","COUNTRY_ID")
Use the following query to find the number of distinct values and find whether a histogram has been created for a column group:
select e.extension col_group, t.num_distinct, t.histogram
2 from user_stat_extensions e, user_tab_col_statistics t
3 where e.extension_name=t.column_name
4 and e.table_name=t.table_name
5 and t.table_name='CUSTOMERS';
COL_GROUP NUM_DISTINCT HISTOGRAM
-----------------------------------------------------------------
("COUNTRY_ID","CUST_STATE_PROVINCE") 145 FREQUENCY
Statistiques d'expressions
Lorsque une fonction est appliquée à une colonne dans la clause "where" (fonction(col1)=constant), l'optimiseur n'a aucun moyen de savoir comment cette fonction affecte la sélectivité de la colonne.
En recueillant les statistiques de cette expression, l'optimiseur obtient une valeur de sélectivité bien plus juste.
Creating Expression Statistics
select
dbms_stats.create_extended_stats(null,'customers',
select
dbms_stats.create_extended_stats(null,'customers',
'(lower(cust_state_province))')
from dual;
Monitoring Expression Statistics
Select extension_name, extension
from user_stat_extensions
where table_name='CUSTOMERS';
EXTENSION_NAME EXTENSION
------------------------------------------------------------------
SYS_STUBPHJSBRKOIK9O2YV3W8HOUE (LOWER("CUST_STATE_PROVINCE"))
Dropping Expression Statistics
exec dbms_stats.drop_extended_stats(null,'customers',
from dual;
Monitoring Expression Statistics
Select extension_name, extension
from user_stat_extensions
where table_name='CUSTOMERS';
EXTENSION_NAME EXTENSION
------------------------------------------------------------------
SYS_STUBPHJSBRKOIK9O2YV3W8HOUE (LOWER("CUST_STATE_PROVINCE"))
Dropping Expression Statistics
exec dbms_stats.drop_extended_stats(null,'customers',
'(lower(country_id))');
exec dbms_stats.gather_table_stats('sh','customers',method_opt =>
'for all columns size skewonly
for columns (cust_state_province,country_id) size skewonly');
et
exec dbms_stats.gather_table_stats('sh','customers', method_opt =>
'for all columns size skewonly
for columns (lower(cust_state_province)) size skewonly');
Nb : skewonly indique à Oracle de ne calculer des histogrammes que pour les colonnes qui ont une répartition très inégale.
Il est possible de calculer directement les statistiques sur une extension (groupe ou fonction) de colonne(s) sans avoir à créer cette extension explicitement, celle-ci l'est implicitement et demeure dans la liste des extension.
Gathering Extended Statistics exec dbms_stats.gather_table_stats('sh','customers',method_opt =>
'for all columns size skewonly
for columns (cust_state_province,country_id) size skewonly');
et
'for all columns size skewonly
for columns (lower(cust_state_province)) size skewonly');
Nb : skewonly indique à Oracle de ne calculer des histogrammes que pour les colonnes qui ont une répartition très inégale.
STATISTIQUES : lock statistics
Le package DBMS_STATS offre la possibilité des verrouiller les statistiques d'une table ou de toutes les tables d'un schéma.
Ceci peut être utile lorsque l'on souhaite que la collecte automatique ne passe pas sur certaines tables dont on maîtrise le chargement et donc le moment opportun pour passer les statistiques.
SQL> drop table couuk8.toto;
Table dropped.
SQL> CREATE TABLE couuk8.toto (
2 a NUMBER
3 );
Table created.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
SQL> -- test 1
SQL> EXEC DBMS_STATS.LOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> col owner for a8 trunc
SQL> col table_name for a10 trunc
SQL> SELECT owner, table_name, stattype_locked
2 FROM dba_tab_statistics
3 WHERE owner = 'COUUK8'
4 AND stattype_locked IS NOT NULL
5 ;
OWNER TABLE_NAME STATT
-------- ---------- -----
COUUK8 TOTO ALL
SQL> EXEC DBMS_STATS.GATHER_SCHEMA_STATS ('COUUK8');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
SQL> -- test 2
SQL> EXEC DBMS_STATS.UNLOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> EXEC DBMS_STATS.GATHER_SCHEMA_STATS ('COUUK8');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
01/08/11
SQL> -- test 3
SQL> EXEC DBMS_STATS.DELETE_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
SQL> EXEC DBMS_STATS.LOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS ('COUUK8','TOTO');
BEGIN DBMS_STATS.GATHER_TABLE_STATS ('COUUK8','TOTO'); END;
*
ERROR at line 1:
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 13437
ORA-06512: at "SYS.DBMS_STATS", line 13457
ORA-06512: at line 1
SQL> -- test 4
SQL> EXEC DBMS_STATS.UNLOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
01/08/11
Ceci peut être utile lorsque l'on souhaite que la collecte automatique ne passe pas sur certaines tables dont on maîtrise le chargement et donc le moment opportun pour passer les statistiques.
Procédures de lock de statistiques :
DBMS_STATS.LOCK_TABLE_STATS (
ownname VARCHAR2,tabname VARCHAR2);
DBMS_STATS.UNLOCK_TABLE_STATS (
ownname VARCHAR2,tabname VARCHAR2);
ownname VARCHAR2,tabname VARCHAR2);
DBMS_STATS.UNLOCK_TABLE_STATS (
ownname VARCHAR2,tabname VARCHAR2);
DBMS_STATS.LOCK_SCHEMA_STATS (
ownname VARCHAR2);
DBMS_STATS.UNLOCK_SCHEMA_STATS (
ownname VARCHAR2);
ownname VARCHAR2);
DBMS_STATS.UNLOCK_SCHEMA_STATS (
ownname VARCHAR2);
Exemples de comportement en LOCK ou UNLOCK :
Table dropped.
SQL> CREATE TABLE couuk8.toto (
2 a NUMBER
3 );
Table created.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
SQL> -- test 1
SQL> EXEC DBMS_STATS.LOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> col owner for a8 trunc
SQL> col table_name for a10 trunc
SQL> SELECT owner, table_name, stattype_locked
2 FROM dba_tab_statistics
3 WHERE owner = 'COUUK8'
4 AND stattype_locked IS NOT NULL
5 ;
OWNER TABLE_NAME STATT
-------- ---------- -----
COUUK8 TOTO ALL
SQL> EXEC DBMS_STATS.GATHER_SCHEMA_STATS ('COUUK8');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
SQL> -- test 2
SQL> EXEC DBMS_STATS.UNLOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> EXEC DBMS_STATS.GATHER_SCHEMA_STATS ('COUUK8');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
01/08/11
SQL> -- test 3
SQL> EXEC DBMS_STATS.DELETE_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
SQL> EXEC DBMS_STATS.LOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS ('COUUK8','TOTO');
BEGIN DBMS_STATS.GATHER_TABLE_STATS ('COUUK8','TOTO'); END;
*
ERROR at line 1:
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 13437
ORA-06512: at "SYS.DBMS_STATS", line 13457
ORA-06512: at line 1
SQL> -- test 4
SQL> EXEC DBMS_STATS.UNLOCK_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS ('COUUK8','TOTO');
PL/SQL procedure successfully completed.
SQL> SELECT last_analyzed FROM dba_tables
2 WHERE table_name='TOTO'
3 AND owner='COUUK8';
LAST_ANA
--------
01/08/11
STATISTIQUES : dynamic sampling
Oracle offre la possibilité à l'optimiseur de calculer ses statistiques à la volée, en utilisant le mécanisme DYNAMIC SAMPLING. Ceci peut se révéler efficace lorsque Oracle détecte que des statistiques sont absentes ou insuffisament précises, en contre-partie, le parsing de la requête durera un peu plus longtemps...
Pour activer ce mécanisme, il faut
SELECT /*+ dynamic_sampling(e 1) */ count(*)
FROM employees e;
Oracle détermine ainsi au moment de la compilation si une requête peut bénéficier du DYNAMIC SAMPLING. Si tel est le cas, un SQL recursif est exécuté pour scanner un échantillon des blocks de la table et comparer les résultats aux valeurs des prédicats de la requête avant de choisir le meilleur plan d'exécution.
Pour activer ce mécanisme, il faut
- soit que le paramètre d'initialisation OPTIMIZER_DYNAMIC_SAMPLING soit positionné à une valeur entre 1 et 10
- soit que le hint /*+ DYNAMIC_SAMPLING(tab value) */ soit indiqué dans une requête.
Exemple :
SELECT /*+ dynamic_sampling(e 1) */ count(*)
FROM employees e;
Oracle détermine ainsi au moment de la compilation si une requête peut bénéficier du DYNAMIC SAMPLING. Si tel est le cas, un SQL recursif est exécuté pour scanner un échantillon des blocks de la table et comparer les résultats aux valeurs des prédicats de la requête avant de choisir le meilleur plan d'exécution.
STATISTIQUES : statistiques système
Statistiques "SYSTEM"
En plus des statistiques sur la composition des données des tables, l'optimiseur Oracle doit connaître l'état du système sur lequel il détermine ses plans d'exécution. Pour ce faire, les statistiques systèmes doivent être calculées et fournies à l'optimiseur.
En plus des statistiques sur la composition des données des tables, l'optimiseur Oracle doit connaître l'état du système sur lequel il détermine ses plans d'exécution. Pour ce faire, les statistiques systèmes doivent être calculées et fournies à l'optimiseur.
Ces statistiques comportent les informations de performances générales de la CPU et des I/O du serveur. Les besoins en CPU et I/O sont estimés par l'optimiseur pour chaque requête, et sont mises en regard des performances du serveur précédemment déterminées par le calcul de statistiques système. Ainsi Oracle est en mesure de choisir le meilleur plan d'exécution.
Oracle analyse l'activité du sytème dans une période de temps donnée (workload statistics) ou simule une activité (noworkload statistics). Les statistiques système sont recueillies par la procédure suivante DBMS_STATS.GATHER_SYSTEM_STATS.
Extrait de la doc Oracle.
Oracle® Database Performance Tuning Guide - 10g Release 2 (10.2)
Chap 14.
Détermination des valeurs des statistiques système :
Statistiques sur les FIXED OBJECTS
Il est également nécessaire de recueillir les statistiques sur les objets fixes "fixed objects" comme les tables de performance dynamiques en utilisant la procédure DBMS_STATS.GATHER_FIXED_OBJECTS_STATS.
Ces objets enregistrent l'activité courante de la base.
Ces statistiques doivent être collectées lorsque la base a une activité représentative.
Statistiques sur les SCHEMAS SYSTEMES
Oracle® Database Performance Tuning Guide - 10g Release 2 (10.2)
Chap 14.
Gathering Workload Statistics
To gather workload statistics, either:
• Run the dbms_stats.gather_system_stats('start') procedure at the beginning of the workload window, then the dbms_stats.gather_system_stats('stop') procedure at the end of the workload window.
• Run dbms_stats.gather_system_stats('interval', interval=>N) where N is the number of minutes when statistics gathering will be stopped automatically.
To delete system statistics, run dbms_stats.delete_system_stats(). Workload statistics will be deleted and reset to the default noworkload statistics.
Gathering Noworkload Statistics
To gather noworkload statistics, run dbms_stats.gather_system_stats() with no arguments. There will be an overhead on the I/O system during the gathering process of noworkload statistics. The gathering process may take from a few seconds to several minutes, depending on I/O performance and database size.The information is analyzed and verified for consistency. In some cases, the value of noworkload statistics may remain its default value. In such cases, repeat the statistics gathering process or set the value manually to values that the I/O system has according to its specifications by using the dbms_stats.set_system_stats procedure.
"Oracle Corporation highly recommends that you gather system statistics"
Détermination des valeurs des statistiques système :
SQL> select version from v$instance;
VERSION
-----------------
10.2.0.4.0
SQL> select pname, pval1 from sys.aux_stats$ where sname = 'SYSSTATS_MAIN';
PNAME PVAL1
------------------------------ ----------
SREADTIM 8,855
MREADTIM 39,009
CPUSPEED 432
MBRC 28
MAXTHR 164772864
SLAVETHR 538624
6 rows selected.
Statistiques sur les FIXED OBJECTS
Il est également nécessaire de recueillir les statistiques sur les objets fixes "fixed objects" comme les tables de performance dynamiques en utilisant la procédure DBMS_STATS.GATHER_FIXED_OBJECTS_STATS.
Ces objets enregistrent l'activité courante de la base.
Ces statistiques doivent être collectées lorsque la base a une activité représentative.
Statistiques sur les SCHEMAS SYSTEMES
Enfin pour calculer les statistiques sur les schémas système (SYS, SYSTEM, CTXSYS, DRSYS), on peut utiliser la procédure DBMS_STATS.GATHER_DICTIONARY_STATS.
STATISTIQUES : collecte automatique
DBMS_STATS
Le package DBMS_STATS est au centre du processus de calcul de statistiques sur les objets de la base Oracle depuis la version Oracle 8i.
Pour les procédures GATHER_SCHEMA_STATS et GATHER_DATABASE_STATS, L'option GATHER_STALE permet de calculer les statistiques devenues obsolètes (contenu des tables modifié de plus de 10%). L'option GATHER_AUTO calcule en plus les statistiques des tables qui n'en n'ont pas (comme l'option GATHER_EMPTY)
NB : L'option GATHER (tout court), indique de recalculer tout le schéma sans autre considération.
MONITORING
Le monitoring est le process qui enregistre le nombre de modifications survenues sur une table.
En 9i, les paramètres MONITORING et NOMONITORING des tables indiquaient si les modifications subies par ces tables devaient être monitorées ou pas.
A partir de la 10g, ce paramètre est obsolète et n'est plus pris en compte. Seul le paramètre d'init STATISTICS_LEVEL commande le monitoring et le calcul de statistiques des objets de la base.
Valeurs possibles :
* ALL (collecte automatique des statistiques activé + statistiques supplémentaires OS et plans). The additional statistics are timed OS statistics and plan execution statistics.
* TYPICAL (valeur par défaut - collect automatique des statistiques activée)
* BASIC (désactive le monitoring des tables ainsi que le calcul automatique des statistiques - non recommandé par Oracle)
Le monitoring des tables, actualise les nombre de lignes mises à jour depuis le dernier calcul de statistiques de cette table. Toutes les 3 heures environ, SMON flush les valeurs de monitoring contenues dans la SGA dans le dictionnaire de données. (vue DBA_TAB_MODIFICATIONS).
Flush manuel :
GATHER_STATS_JOB
Le job GATHER_STATS_JOB est prédéfini lors de la création de la base Oracle. Il se charge de collecter les statistiques pour tous les objets de la base pour lesquels il n'y a pas de statistiques ou pour lesquels les statistiques sont obsolètes.
Le scheduler exécute ce job lorsque la fenêtre de maintenance est ouverte. (Par défaut tous les jours de 22H00 à 06H00) - Le job s'interrompt à la fermeture de la fenêtre de maintenance. Le Job appelle la procédure DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC. Cette procédure est similaire à la procédure DBMS_STATS.GATHER_DATABASE_STATS, mais en plus elle priorise les objets à analyser.
Lister le job GATHER_STATS_JOB :
Si l'on souhaite collecter les statistiques manuellement et donc désactiver le calcul automatique :
En 11g deux nouveaux concepts sont introduits :
- prise de statistiques sur des colonnes multiples et sur des fonctions de colonnes
- possibilité de publier les statistiques
Le package DBMS_STATS est au centre du processus de calcul de statistiques sur les objets de la base Oracle depuis la version Oracle 8i.
Pour les procédures GATHER_SCHEMA_STATS et GATHER_DATABASE_STATS, L'option GATHER_STALE permet de calculer les statistiques devenues obsolètes (contenu des tables modifié de plus de 10%). L'option GATHER_AUTO calcule en plus les statistiques des tables qui n'en n'ont pas (comme l'option GATHER_EMPTY)
GATHER_AUTO = GATHER_STALE + GATHER_EMPTY
NB : L'option GATHER (tout court), indique de recalculer tout le schéma sans autre considération.
MONITORING
Le monitoring est le process qui enregistre le nombre de modifications survenues sur une table.
En 9i, les paramètres MONITORING et NOMONITORING des tables indiquaient si les modifications subies par ces tables devaient être monitorées ou pas.
A partir de la 10g, ce paramètre est obsolète et n'est plus pris en compte. Seul le paramètre d'init STATISTICS_LEVEL commande le monitoring et le calcul de statistiques des objets de la base.
Valeurs possibles :
* ALL (collecte automatique des statistiques activé + statistiques supplémentaires OS et plans). The additional statistics are timed OS statistics and plan execution statistics.
* TYPICAL (valeur par défaut - collect automatique des statistiques activée)
* BASIC (désactive le monitoring des tables ainsi que le calcul automatique des statistiques - non recommandé par Oracle)
Le monitoring des tables, actualise les nombre de lignes mises à jour depuis le dernier calcul de statistiques de cette table. Toutes les 3 heures environ, SMON flush les valeurs de monitoring contenues dans la SGA dans le dictionnaire de données. (vue DBA_TAB_MODIFICATIONS).
Flush manuel :
EXEC dbms_stats.flush_database_monitoring_info;
GATHER_STATS_JOB
Le job GATHER_STATS_JOB est prédéfini lors de la création de la base Oracle. Il se charge de collecter les statistiques pour tous les objets de la base pour lesquels il n'y a pas de statistiques ou pour lesquels les statistiques sont obsolètes.
Le scheduler exécute ce job lorsque la fenêtre de maintenance est ouverte. (Par défaut tous les jours de 22H00 à 06H00) - Le job s'interrompt à la fermeture de la fenêtre de maintenance. Le Job appelle la procédure DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC. Cette procédure est similaire à la procédure DBMS_STATS.GATHER_DATABASE_STATS, mais en plus elle priorise les objets à analyser.
Lister le job GATHER_STATS_JOB :
SELECT *
FROM DBA_SCHEDULER_JOBS
FROM DBA_SCHEDULER_JOBS
WHERE JOB_NAME = 'GATHER_STATS_JOB';
Si l'on souhaite collecter les statistiques manuellement et donc désactiver le calcul automatique :
en 10g :
BEGIN
DBMS_SCHEDULER.DISABLE('GATHER_STATS_JOB');
END;
/
en 11g :
BEGIN
DBMS_AUTO_TASK_ADMIN.DISABLE(
client_name => 'auto optimizer stats collection',
operation => NULL,
window_name => NULL);
END;
/
En 11g deux nouveaux concepts sont introduits :
- prise de statistiques sur des colonnes multiples et sur des fonctions de colonnes
- possibilité de publier les statistiques
mercredi 27 juillet 2011
TRUNCATE : Appel de Procédure Stockée via db link
Alors que le delete, lui, fonctionne sans problème, la commande truncate en tant que DDL ne passe pas au travers d'un db link.
Sur la base cible :
SQL> SELECT COUNT(*) FROM toto;
COUNT(*)
----------
1
COUNT(*)
----------
1
Sur la base source :
SQL> DELETE FROM toto@versqua;
1 row deleted.
SQL> ROLLBACK;
Rollback complete.
SQL> TRUNCATE TABLE toto@versqua;
TRUNCATE TABLE toto@versqua
*
ERROR at line 1:
ORA-02021: DDL operations are not allowed on a remote database
Il existe une solution qui consiste à disposer une procédure stockée sur la base cible qui effectue le truncate. Cette procédure étant appelée depuis la base source via db link.
Sur la base cible :
SQL> SELECT COUNT(*) FROM toto;
COUNT(*)
----------
1
COUNT(*)
----------
1
SQL> CREATE PROCEDURE p_u_truncate (p_table IN VARCHAR2)
2 IS
3 BEGIN
4 EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || p_table;
5 END;
6 /
Procedure created.
2 IS
3 BEGIN
4 EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || p_table;
5 END;
6 /
Procedure created.
Sur la base source :
SQL> EXEC p_u_truncate@versqua('TOTO');
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
Sur la base cible :
SQL> SELECT COUNT(*) FROM toto;
COUNT(*)
----------
0
COUNT(*)
----------
0
Explication : la procédure stockée sur la base cible est appelée depuis la base source et s'exécute sur la base cible.
mardi 19 juillet 2011
DOUBLONS : détecter les doublons partiels
Comment détecter les lignes d'une table faisant doublon sur une liste de colonne (et non pas sur l'ensemble de la ligne).
Exemple :
SQL> CREATE TABLE toto (
2 pays VARCHAR2(3),
3 ville VARCHAR2(10),
4 note NUMBER
5 );
Table created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',1);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',2);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',3);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',3);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','LYON',4);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','LYON',5);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','MARSEILLE',5);
1 row created.
SQL> INSERT INTO toto VALUES ('USA','NEW YORK',5);
1 row created.
SQL> COMMIT;
Commit complete.
2 pays VARCHAR2(3),
3 ville VARCHAR2(10),
4 note NUMBER
5 );
Table created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',1);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',2);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',3);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','PARIS',3);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','LYON',4);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','LYON',5);
1 row created.
SQL> INSERT INTO toto VALUES ('FRA','MARSEILLE',5);
1 row created.
SQL> INSERT INTO toto VALUES ('USA','NEW YORK',5);
1 row created.
SQL> COMMIT;
Commit complete.
Pour le cas classique :
SQL> SELECT t1.pays, t1.ville, t1.note, count(*)
2 FROM toto t1
3 GROUP BY t1.pays, t1.ville, t1.note
4 HAVING COUNT(*) > 1
5 ;
PAY VILLE NOTE COUNT(*)
--- ---------- ---------- ----------
FRA PARIS 3 2
Et la liste :
2 FROM toto t1
3 GROUP BY t1.pays, t1.ville, t1.note
4 HAVING COUNT(*) > 1
5 ;
PAY VILLE NOTE COUNT(*)
--- ---------- ---------- ----------
FRA PARIS 3 2
Et la liste :
SQL> SELECT t1.* FROM toto t1 WHERE t1.rowid >= (
2 SELECT MIN(t2.rowid) FROM toto t2
3 WHERE t1.pays=t2.pays
4 AND t1.ville=t2.ville
5 AND t1.note=t2.note
6 HAVING COUNT(*) > 1
7 )
8 ;
PAY VILLE NOTE
--- ---------- ----------
FRA PARIS 3
FRA PARIS 3
Et par extension, pour le cas des doublons partiels :
SQL> SELECT t1.pays, t1.ville, COUNT(*)
2 FROM toto t1
3 GROUP BY t1.pays, t1.ville
4 HAVING COUNT(*) > 1
5 ;
PAY VILLE COUNT(*)
--- ---------- ----------
FRA LYON 2
FRA PARIS 4
Et la liste :
SQL> SELECT t1.* FROM toto t1 WHERE t1.rowid >= (
2 SELECT MIN(t2.rowid) FROM toto t2
3 WHERE t1.pays=t2.pays
4 AND t1.ville=t2.ville
5 HAVING COUNT(*) > 1
6 )
7 ;
PAY VILLE NOTE
--- ---------- ----------
FRA PARIS 1
FRA PARIS 2
FRA PARIS 3
FRA PARIS 3
FRA LYON 4
FRA LYON 5
2 FROM toto t1
3 GROUP BY t1.pays, t1.ville
4 HAVING COUNT(*) > 1
5 ;
PAY VILLE COUNT(*)
--- ---------- ----------
FRA LYON 2
FRA PARIS 4
Et la liste :
SQL> SELECT t1.* FROM toto t1 WHERE t1.rowid >= (
2 SELECT MIN(t2.rowid) FROM toto t2
3 WHERE t1.pays=t2.pays
4 AND t1.ville=t2.ville
5 HAVING COUNT(*) > 1
6 )
7 ;
PAY VILLE NOTE
--- ---------- ----------
FRA PARIS 1
FRA PARIS 2
FRA PARIS 3
FRA PARIS 3
FRA LYON 4
FRA LYON 5
APPEND NOLOGGING : accélérer les insertions
Pour les chargements massifs, il peut être intéressant d'utiliser certaines options qui permettent d'accélérer le temps de traitement. Bien sûr ces options ont des contre-parties.
L'option APPEND pour l'insert force Oracle à passer directement au-delà du HWM (High Water Mark) pour insérer les nouvelles lignes. Elle évite ainsi la recherche des blocks libres et réduit fortement le nombre de blocks à sauvegarder dans les UNDO. Mais elle conduit à une éventuelle perte d'espace disque.
L'option NOLOGGING indique à Oracle qu'il n'est pas nécessaire de logguer dans les REDO les changements qui ont lieu dans la table. Il s'ensuit une écriture moins importante dans les REDO, mais également l'impossibilité de récupérer la table avec les nouvelles lignes en cas de restauration de cette dernière.
Exemple
Préparation :
SQL> CREATE TABLE toto AS
2 SELECT rownum ID, dbms_random.string('A', 12) STR,
3 trunc(dbms_random.value(1,1000)) NUM
4 FROM all_objects
5 WHERE rownum <= 200000
6 ;
Table created.
2 SELECT rownum ID, dbms_random.string('A', 12) STR,
3 trunc(dbms_random.value(1,1000)) NUM
4 FROM all_objects
5 WHERE rownum <= 200000
6 ;
Table created.
SQL> INSERT INTO toto SELECT * FROM toto;
200000 rows created.
SQL> INSERT INTO toto SELECT * FROM toto;
400000 rows created.
SQL> COMMIT;
Commit complete.
200000 rows created.
SQL> INSERT INTO toto SELECT * FROM toto;
400000 rows created.
SQL> COMMIT;
Commit complete.
Session 1 :
SQL> SET TIMING ON
SQL> INSERT INTO toto SELECT * FROM toto;
800000 rows created.
Elapsed: 00:00:31.04
SQL> ROLLBACK;
Rollback complete.
Elapsed: 00:00:02.09
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'undo change vector size';
NAME VALUE
----------------------------------------------------- ----------
undo change vector size 2270900
SQL> INSERT INTO toto SELECT * FROM toto;
800000 rows created.
Elapsed: 00:00:31.04
SQL> ROLLBACK;
Rollback complete.
Elapsed: 00:00:02.09
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'undo change vector size';
NAME VALUE
----------------------------------------------------- ----------
undo change vector size 2270900
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'rollback changes - undo records applied';
NAME VALUE
----------------------------------------------------- ----------
rollback changes - undo records applied 6087
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'redo size';
NAME VALUE
----------------------------------------------------- ----------
redo size 31320224
NAME VALUE
----------------------------------------------------- ----------
rollback changes - undo records applied 6087
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'redo size';
NAME VALUE
----------------------------------------------------- ----------
redo size 31320224
Session 2 :
SQL> SET TIMING ON
SQL> INSERT /*+ APPEND */ INTO toto SELECT * FROM toto;
800000 rows created.
Elapsed: 00:00:12.06
SQL> INSERT /*+ APPEND */ INTO toto SELECT * FROM toto;
800000 rows created.
Elapsed: 00:00:12.06
SQL> ROLLBACK;
Rollback complete.
Elapsed: 00:00:00.00
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'undo change vector size';
NAME VALUE
----------------------------------------------------- ----------
undo change vector size 258516
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'rollback changes - undo records applied';
NAME VALUE
----------------------------------------------------- ----------
rollback changes - undo records applied 0
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'redo size';
NAME VALUE
----------------------------------------------------- ----------
redo size 25438660
Rollback complete.
Elapsed: 00:00:00.00
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'undo change vector size';
NAME VALUE
----------------------------------------------------- ----------
undo change vector size 258516
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'rollback changes - undo records applied';
NAME VALUE
----------------------------------------------------- ----------
rollback changes - undo records applied 0
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'redo size';
NAME VALUE
----------------------------------------------------- ----------
redo size 25438660
Session 3 :
SQL> SET TIMING ON
SQL> INSERT /*+ APPEND NOLOGGING */ INTO toto SELECT * FROM toto;
800000 rows created.
Elapsed: 00:00:04.01
SQL> INSERT /*+ APPEND NOLOGGING */ INTO toto SELECT * FROM toto;
800000 rows created.
Elapsed: 00:00:04.01
SQL> ROLLBACK;
Rollback complete.
Elapsed: 00:00:00.00
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'undo change vector size';
NAME VALUE
----------------------------------------------------- ----------
undo change vector size 208
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'rollback changes - undo records applied';
NAME VALUE
----------------------------------------------------------------
rollback changes - undo records applied 0
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'redo size';
NAME VALUE
----------------------------------------------------- ----------
redo size 24488552
Rollback complete.
Elapsed: 00:00:00.00
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'undo change vector size';
NAME VALUE
----------------------------------------------------- ----------
undo change vector size 208
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'rollback changes - undo records applied';
NAME VALUE
----------------------------------------------------------------
rollback changes - undo records applied 0
SQL> SELECT name, value FROM v$mystat NATURAL JOIN v$statname WHERE name = 'redo size';
NAME VALUE
----------------------------------------------------- ----------
redo size 24488552
en résumé :
APPEND NOLOGGING INSERT(sec) UNDO_VECTOR REDO_SIZE
N N 31 2270900 31320224
Y N 12 258516 25438660
Y Y 4 208 24488552
ENABLE ROW MOVEMENT : tables partitionnées
Oracle garantit qu'une ligne ne sera pas déplacée physiquement par un update et donc que son ROWID ne sera pas changé. Ce phénomène pourrait en effet survenir dans une table partitionnée avec un update sur la colonne de partitionnement, forçant Oracle à déplacer la ligne dans la bonne partition. (Déplacement physique, changement de ROWID).
Exemple :
SQL> CREATE TABLE toto(a NUMBER)
2 PARTITION BY RANGE (a)(
3 PARTITION p1 VALUES LESS THAN (10),
4 PARTITION p2 VALUES LESS THAN (20),
5 PARTITION p3 VALUES LESS THAN (MAXVALUE));
Table created.
SQL> INSERT INTO toto VALUES (5);
1 row created.
SQL> UPDATE toto SET a=15;
UPDATE toto SET a=15
*
ERROR at line 1:
ORA-14402: updating partition key column would cause a partition change
2 PARTITION BY RANGE (a)(
3 PARTITION p1 VALUES LESS THAN (10),
4 PARTITION p2 VALUES LESS THAN (20),
5 PARTITION p3 VALUES LESS THAN (MAXVALUE));
Table created.
SQL> INSERT INTO toto VALUES (5);
1 row created.
SQL> UPDATE toto SET a=15;
UPDATE toto SET a=15
*
ERROR at line 1:
ORA-14402: updating partition key column would cause a partition change
Pourquoi Oracle provoque-t'il cette erreur ?
A priori on ne voit pas de raison pour laquelle on ne pourrait pas tranquillement mettre à jour une table, même sur la colonne de partitionnement, sans provoquer une erreur... Ceci est en fait un héritage, qu'Oracle nous a transmis pour que lors de l'introduction de tables partitionnées, les programmes qui utilisaient les ROWID n'aient pas de mauvaise surprise en partitionnant leurs tables. Le changement du ROWID lors d'un update pouvant être catastrophique avec ce genre de conception de programme.
Mais comment faire alors ?
Par défaut, donc, Oracle empêche les mises à jour des colonnes de partitionnement des tables partitionnées. Pour autoriser ces mises à jour, il est donc nécessaire de passer la commande ENABLE ROW MOVEMENT en préalable :
Exemple :
SQL> ALTER TABLE toto ENABLE ROW MOVEMENT;
Table altered.
SQL> UPDATE toto SET a=15;
1 row updated.
Table altered.
SQL> UPDATE toto SET a=15;
1 row updated.
Bien qu'il existe la commande inverse qui rétablit l'impossibilité de mettre à jour ces colonnes (DISABLE ROW MOVEMENT), si aucun de vos programmes n'utilise les ROWID, il n'est pas nécessaire de repasser les tables en mode par défaut (disable).
Voici tout de même la commande :
SQL> ALTER TABLE toto DISABLE ROW MOVEMENT;
Table altered.
Table altered.
mardi 12 juillet 2011
VARIABLES : bind, substitution, programme
Dans cet exemple sont illustrés les emplois des différents types de variable suivants :
- variable de substitution (define) - interprétée par SqlPlus
- bind variable (variable) - SQL - PL/SQL
- variable de programme (declare) - PL/SQL
- variable de programme (declare) - PL/SQL
Ces variables sont utilisées pour des raisons bien spécifiques, et ne doivent en aucun cas être confondues...
Exemple
SQL> SET VERIFY OFF
SQL> DEFINE matable=dba_users;
SQL> VARIABLE b NUMBER;
SQL> DECLARE
2 res VARCHAR2(30);
3
4 BEGIN
5 :b :=0;
6 SELECT username INTO res FROM &matable WHERE user_id=:b;
7 dbms_output.put_line('USER IS : '||res);
8 END;
9 /
USER IS : SYS
PL/SQL procedure successfully completed.
SQL> DEFINE matable=dba_users;
SQL> VARIABLE b NUMBER;
SQL> DECLARE
2 res VARCHAR2(30);
3
4 BEGIN
5 :b :=0;
6 SELECT username INTO res FROM &matable WHERE user_id=:b;
7 dbms_output.put_line('USER IS : '||res);
8 END;
9 /
USER IS : SYS
PL/SQL procedure successfully completed.
Autre exemple pour une bind variable en SQL pur
SQL> VARIABLE b NUMBER;
SQL> EXECUTE :b:=0;
PL/SQL procedure successfully completed.
SQL> SELECT username
2 FROM all_users
3 WHERE user_id = :b;
USERNAME
------------------------------
SYS
SQL> EXECUTE :b:=0;
PL/SQL procedure successfully completed.
SQL> SELECT username
2 FROM all_users
3 WHERE user_id = :b;
USERNAME
------------------------------
SYS
OUT : valeur de retour d'une procedure
Les procédures Oracle offrent la possibilité de renvoyer une valeur en sortie au travers d'une variable OUT définie dans la liste des paramètres de la procédure.
CREATE OR REPLACE PROCEDURE p_sqrt(
a IN NUMBER, b OUT NUMBER
) AS
BEGIN
b:=a*a;
END;
/
SET SERVEROUTPUT ON SIZE 2000
DECLARE
entree NUMBER;
sortie NUMBER;
BEGIN
entree:=9;
p_sqrt(entree,sortie);
dbms_output.put_line(entree||'² = '||sortie);
END;
/
a IN NUMBER, b OUT NUMBER
) AS
BEGIN
b:=a*a;
END;
/
SET SERVEROUTPUT ON SIZE 2000
DECLARE
entree NUMBER;
sortie NUMBER;
BEGIN
entree:=9;
p_sqrt(entree,sortie);
dbms_output.put_line(entree||'² = '||sortie);
END;
/
9² = 81
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
CUBE : requête analytique
Il existe de nombreuses fonctions analytiques permettant d'obtenir des rapports multi-dimensionnels plus ou moins complexes.
Exemple de Cube :
SET LINES 150
SET PAGES 30
SET FEED OFF
SET ESCAPE \
BREAK ON typ SKIP 1 ON tbs SKIP 0
COL tbs FOR A10 TRUNC
SELECT
NVL(t.typ,'ALL') AS typ,
NVL(t.tablespace_name,'________') AS tbs,
ROUND(SUM(t.bytes)/1024/1024,0) AS siz_mo
FROM (
SELECT
(CASE
WHEN s.segment_type LIKE 'TAB%' THEN 'TAB'
WHEN s.segment_type LIKE 'IND%' THEN 'IND'
ELSE 'OTH'
END
) AS typ,
s.tablespace_name,
s.bytes
FROM dba_segments S
WHERE owner='SYS'
) T
GROUP BY CUBE(t.typ, t.tablespace_name)
ORDER BY 1 DESC, 2 DESC
/
SET FEED ON
SET ESCAPE OFF
SET PAGES 30
SET FEED OFF
SET ESCAPE \
BREAK ON typ SKIP 1 ON tbs SKIP 0
COL tbs FOR A10 TRUNC
SELECT
NVL(t.typ,'ALL') AS typ,
NVL(t.tablespace_name,'________') AS tbs,
ROUND(SUM(t.bytes)/1024/1024,0) AS siz_mo
FROM (
SELECT
(CASE
WHEN s.segment_type LIKE 'TAB%' THEN 'TAB'
WHEN s.segment_type LIKE 'IND%' THEN 'IND'
ELSE 'OTH'
END
) AS typ,
s.tablespace_name,
s.bytes
FROM dba_segments S
WHERE owner='SYS'
) T
GROUP BY CUBE(t.typ, t.tablespace_name)
ORDER BY 1 DESC, 2 DESC
/
SET FEED ON
SET ESCAPE OFF
Résultat :
TYP TBS SIZ_MO
--- ---------- ----------
TAB TOOLS 23
SYSTEM 402
SYSAUX 205
________ 629
OTH UNDOTBS 52
SYSTEM 218
SYSAUX 21
________ 291
IND TOOLS 0
SYSTEM 370
SYSAUX 283
________ 654
ALL UNDOTBS 52
TOOLS 23
SYSTEM 990
SYSAUX 508
________ 1573
--- ---------- ----------
TAB TOOLS 23
SYSTEM 402
SYSAUX 205
________ 629
OTH UNDOTBS 52
SYSTEM 218
SYSAUX 21
________ 291
IND TOOLS 0
SYSTEM 370
SYSAUX 283
________ 654
ALL UNDOTBS 52
TOOLS 23
SYSTEM 990
SYSAUX 508
________ 1573
Inscription à :
Articles (Atom)