Transposition de tables

Ce que je souhaite faire

Je souhaite rendre lisible sous excel une table historique, par exemple pour dbs_hist_snapshot, je souhaite voir pour le top 10 des requêtes consommatrices en temps elapse les différents temps elapse pour une fenêtre de snapshots … idéalement je souhaite avoir en en tête de ligne le sql_id, en en tête de colonne le snap_id et dans les cases du tableau les valeurs de temps elapse pour la requêete au moment du snapshot … Un petit schéma ?

SQL_ID             TOTAL      C_834      C_835      C_836      C_837      C_838      C_839      C_840
------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
6gvch1xu9ca3g   42391392   13013927    5392808    5393650    4953967    3261169    4969584    5406287
459f3z9u4fb3u   23436549   13594062    1852118    2019437    1463763    1243657    1659715    1603797
7mdacxfm37utk   17700940    3612265    2736617    2562545    2386164    1282571    2257281    2863497
2b064ybzkwf1y    7346698    1214429    1087398    1132594    1069030     781162    1021148    1040937
70vs1d7ywk5m0    5545662    5545662          0          0          0          0          0          0
a8j39qb13tqkr    5468028    2037413     585054     672582     653276     451146     484936     583621
3am9cfkvx7gq1    5258369     906223     765851     693923     830057     601760     762278     698277
6v7n0y2bq89n8    4980042     970111     742508     774718     700769     417384     657582     716970
2whm2vvjb98k7    4015376    4015376          0          0          0          0          0          0

Je ne travaillerai qu’avec des colonnes centrales numériques, sinon ça ne fonctionne pas …

Générer la requête

J’utilise un code PL/SQL pour générer la requête :

create or replace function f_trsp   ( lig   in varchar2
                                        , col   in varchar2
                                        , ltab  in varchar2
                                        , pivot in varchar2
     , owner in varchar2 
     , ctab  in varchar2 
     , mini  in varchar2 
     , maxi  in varchar2 ) return clob is
v_col   varchar2(32767);
v_where varchar2(32767);
v_owner varchar2(255);
c1 sys_refcursor;
v_poreq varchar2(32767);
v_tab   varchar2(32767);
req     clob;
begin
 req:='
Select '||lig||'
     , sum('||pivot||') total ';
 
 if owner is null
 then 
  select sys_context('USERENV','CURRENT_SCHEMA') into v_owner from sys.dual;
 else
  v_owner:=owner;
 end if;
 
 if ( mini is not null or maxi is not null )
 then
  v_where:=' where ';
  if mini is not null 
  then
   v_where:=v_where||col||' >
= '||mini ;
   if maxi is not null
   then
       v_where:=v_where||' and '||col||' <= '||maxi ;
   end if;
  else
      v_where:=v_where||col||' <= '||maxi ;
  end if;
 end if;
 
 if ctab is null 
 then
  v_tab:=ltab;
 else
  v_tab:=ctab;
 end if;
   
 open c1 for 'select distinct to_char('||col||') from '||v_owner||'.'||nvl(ctab,ltab)||v_where||' order by 1' ;
 loop
  fetch c1 into v_col;
  exit when c1%notfound;
  v_poreq:='
     , sum(decode('||col||','||v_col||','||pivot||',0)) c_'||trim(v_col) ;
  dbms_lob.writeappend(req,length(v_poreq),v_poreq);
 end loop;
 close c1;
 v_poreq:='
from '||v_owner||'.'||ltab||v_where||'
group by '||lig||'
order by 2 desc';
 dbms_lob.writeappend(req,length(v_poreq),v_poreq);
        return req;
end f_trsp;
/

J’execute le fonction en limitant le nombre de lignes retournées (pour avoir le “top n”

set lines 155 pages 10000 long 999999 longc 999999

desc f_trsp
FUNCTION f_trsp RETURNS CLOB
 Nom d'argument                  Type                    E/S par défaut ?
 ------------------------------ ----------------------- ------ --------
 LIG                            VARCHAR2                IN
 COL                            VARCHAR2                IN
 LTAB                           VARCHAR2                IN
 PIVOT                          VARCHAR2                IN
 OWNER                          VARCHAR2                IN
 CTAB                           VARCHAR2                IN
 MINI                           VARCHAR2                IN
 MAXI                           VARCHAR2                IN


select 'select * from ('||f_trsp('sql_id'
                               , 'snap_id'
                               , 'dba_hist_sqlstat'
                               , 'ELAPSED_TIME_DELTA'
                               , 'sys'
                               , 'dba_hist_snapshot'
                               , '834'
                               , '840')||')
 where rownum < 10 ' from dual
/

'SELECT*FROM('||F_TRSP('SQL_ID','SNAP_ID','DBA_HIST_SQLSTAT','ELAPSED_TIME_D
----------------------------------------------------------------------------
select * from (
Select sql_id
     , sum(ELAPSED_TIME_DELTA) total
     , sum(decode(snap_id,834,ELAPSED_TIME_DELTA,0)) c_834
     , sum(decode(snap_id,835,ELAPSED_TIME_DELTA,0)) c_835
     , sum(decode(snap_id,836,ELAPSED_TIME_DELTA,0)) c_836
     , sum(decode(snap_id,837,ELAPSED_TIME_DELTA,0)) c_837
     , sum(decode(snap_id,838,ELAPSED_TIME_DELTA,0)) c_838
     , sum(decode(snap_id,839,ELAPSED_TIME_DELTA,0)) c_839
     , sum(decode(snap_id,840,ELAPSED_TIME_DELTA,0)) c_840
from sys.dba_hist_sqlstat where snap_id >
= 834 and snap_id <= 840
group by sql_id
order by 2 desc)
 where rownum < 10

Ecoulé : 00 :00 :00.93

Il “suffit” ensuite d’exécuter la requête pour obtenir le tableau

set autotrace on
set timi on

select * from (
Select sql_id
     , sum(ELAPSED_TIME_DELTA) total
     , sum(decode(snap_id,834,ELAPSED_TIME_DELTA,0)) c_834
     , sum(decode(snap_id,835,ELAPSED_TIME_DELTA,0)) c_835
     , sum(decode(snap_id,836,ELAPSED_TIME_DELTA,0)) c_836
     , sum(decode(snap_id,837,ELAPSED_TIME_DELTA,0)) c_837
     , sum(decode(snap_id,838,ELAPSED_TIME_DELTA,0)) c_838
     , sum(decode(snap_id,839,ELAPSED_TIME_DELTA,0)) c_839
     , sum(decode(snap_id,840,ELAPSED_TIME_DELTA,0)) c_840
from sys.dba_hist_sqlstat where snap_id >
= 834 and snap_id <= 840
group by sql_id
order by 2 desc)
where rownum < 10
/

SQL_ID             TOTAL      C_834      C_835      C_836      C_837      C_838      C_839      C_840
------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
6gvch1xu9ca3g   42391392   13013927    5392808    5393650    4953967    3261169    4969584    5406287
459f3z9u4fb3u   23436549   13594062    1852118    2019437    1463763    1243657    1659715    1603797
7mdacxfm37utk   17700940    3612265    2736617    2562545    2386164    1282571    2257281    2863497
2b064ybzkwf1y    7346698    1214429    1087398    1132594    1069030     781162    1021148    1040937
70vs1d7ywk5m0    5545662    5545662          0          0          0          0          0          0
a8j39qb13tqkr    5468028    2037413     585054     672582     653276     451146     484936     583621
3am9cfkvx7gq1    5258369     906223     765851     693923     830057     601760     762278     698277
6v7n0y2bq89n8    4980042     970111     742508     774718     700769     417384     657582     716970
2whm2vvjb98k7    4015376    4015376          0          0          0          0          0          0

9 ligne(s) sélectionnée(s).

Ecoulé : 00 :00 :00.54

Plan hash value: 3078857970

---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name             | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                  |     2 |   224 |     6  (34)| 00:00:01 |       |       |
|*  1 |  COUNT STOPKEY                         |                  |       |       |            |       |          |       |
|   2 |   VIEW                                 |                  |     2 |   224 |     6  (34)| 00:00:01 |       |       |
|*  3 |    SORT ORDER BY STOPKEY               |                  |     2 |    98 |     6  (34)| 00:00:01 |       |       |
|   4 |     HASH GROUP BY                      |                  |     2 |    98 |     6  (34)| 00:00:01 |       |       |
|   5 |      NESTED LOOPS                      |                  |       |       |            |       |          |       |
|   6 |       NESTED LOOPS                     |                  |     2 |    98 |     4   (0)| 00:00:01 |       |       |
|*  7 |        TABLE ACCESS BY INDEX ROWID     | WRM$_SNAPSHOT    |     1 |    16 |     2   (0)| 00:00:01 |       |       |
|*  8 |         INDEX SKIP SCAN                | WRM$_SNAPSHOT_PK |     1 |       |     1   (0)| 00:00:01 |       |       |
|   9 |        PARTITION RANGE AND             |                  |     1 |       |     1   (0)| 00:00:01 |KEY(AP)|KEY(AP)|
|* 10 |         INDEX RANGE SCAN               | WRH$_SQLSTAT_PK  |     1 |       |     1   (0)| 00:00:01 |KEY(AP)|KEY(AP)|
|  11 |       TABLE ACCESS BY LOCAL INDEX ROWID| WRH$_SQLSTAT     |     2 |    66 |     2   (0)| 00:00:01 |     1 |     1 |
---------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM<10)
   3 - filter(ROWNUM<10)
   7 - filter("SN"."STATUS"=0)
   8 - access("SN"."SNAP_ID">
=834 AND "SN"."SNAP_ID"<=840)
       filter("SN"."SNAP_ID">
=834 AND "SN"."SNAP_ID"<=840)
  10 - access("SN"."DBID"="SQL"."DBID" AND "SN"."SNAP_ID"="SQL"."SNAP_ID" AND
              "SN"."INSTANCE_NUMBER"="SQL"."INSTANCE_NUMBER")
       filter("SQL"."SNAP_ID">
=834 AND "SQL"."SNAP_ID"<=840)


Statistiques
----------------------------------------------------------
          8  recursive calls
          0  db block gets
        434  consistent gets
          0  physical reads
          0  redo size
       1463  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          9  rows processed

C’est efficace, le requête est exécutée en 54 centièmes de secondes, et son plan d’exécution est satisfaisant … bon reste maintenant à exploiter ce type de tables.

C’est sympas mais …

Mon top N est par rapport aux lignes … Mettons que je veuilles transposer la table résultat (je veux dire vraiment transposer, comme dans un tableur). C’est à dire dire je veux un snap par ligne, et N colonnes, qui vont donner les N SQL_id les plus importants sur la fenêtre de snaps choisie … C’est un poil plus compliqué, mais peut être pas tant que ça.

create or replace function f_trsp   ( lig   in varchar2
                                        , col   in varchar2
                                        , ltab  in varchar2
                                        , pivot in varchar2
     , owner in varchar2 
     , ctab  in varchar2 
     , mini  in varchar2 
     , maxi  in varchar2
                                        , topn  in number
                                        , cumul in varchar2 := 'No') return clob is
v_col   varchar2(32767);
v_where varchar2(32767);
v_wtopn varchar2(1000);
v_owner varchar2(255);
c1 sys_refcursor;
v_poreq varchar2(32767);
v_tab   varchar2(32767);
req     clob;
begin
 
        if topn is null
        then
          v_wtopn:='';
        else
          v_wtopn:=' where rownum <= '||topn;
        end if;
        
 if owner is null
 then 
  select sys_context('USERENV','CURRENT_SCHEMA') into v_owner from sys.dual;
 else
  v_owner:=owner;
 end if;
 
 if ( mini is not null or maxi is not null )
 then
  v_where:=' where ';
  if mini is not null 
  then
   v_where:=v_where||col||' >
= '||mini ;
   if maxi is not null
   then
       v_where:=v_where||' and '||col||' <= '||maxi ;
   end if;
  else
      v_where:=v_where||col||' <= '||maxi ;
  end if;
 end if;
 
 if ctab is null 
 then
  v_tab:=ltab;
 else
  v_tab:=ctab;
 end if;
 
        if upper(cumul) = 'NO'
        then
          req:='select * from (Select '||lig||'
    , sum('||pivot||') total ';
          open c1 for 'select distinct to_char('||col||') from '||v_owner||'.'||nvl(ctab,ltab)||v_where||' order by 1' ;
          loop
                  fetch c1 into v_col;
                  exit when c1%notfound;
                  v_poreq:='
       , sum(decode(to_char('||col||'),'''||v_col||''','||pivot||',0)) c_'||trim(v_col) ;
                  dbms_lob.writeappend(req,length(v_poreq),v_poreq);
          end loop;
  -- ncol:=c1%rowcount+2;
          close c1;
          v_poreq:='
  from '||v_owner||'.'||ltab||v_where||'
  group by '||lig||'
  order by 2 desc)'||v_wtopn ;
          dbms_lob.writeappend(req,length(v_poreq),v_poreq);
        else
          req:='Select '||col||' ';
          open c1 for 'select ent from (select distinct to_char('||lig||') ent, sum('||pivot||') tot from '||v_owner||'.'||ltab||v_where||' group by to_char('||lig||') order by 2 desc)'||v_wtopn ;
          loop
                  fetch c1 into v_col;
                  exit when c1%notfound;
                  v_poreq:='
       , sum(decode(to_char('||lig||'),'''||v_col||''','||pivot||',0)) c_'||trim(v_col) ;
                  dbms_lob.writeappend(req,length(v_poreq),v_poreq);
          end loop;
  -- ncol:=c1%rowcount+2;
          close c1;
          v_poreq:='
  from '||v_owner||'.'||ltab||v_where||'
  group by '||col||'
  order by 1 asc';
          dbms_lob.writeappend(req,length(v_poreq),v_poreq);
        end if;        
        return req;
end f_trsp;
/

testons après les flush mémoire qui vont bien.

select f_trsp('sql_id'
                               , 'snap_id'
                               , 'dba_hist_sqlstat'
                               , 'ELAPSED_TIME_DELTA'
                               , 'sys'
                               , 'dba_hist_snapshot'
                               , '834'
                               , '840'
                               , 9
                               , 'No') lig from dual
/

LIG
------------------------------------------------------------------------------
select * from (Select sql_id
    , sum(ELAPSED_TIME_DELTA) total
       , sum(decode(to_char(snap_id),'834',ELAPSED_TIME_DELTA,0)) c_834
       , sum(decode(to_char(snap_id),'835',ELAPSED_TIME_DELTA,0)) c_835
       , sum(decode(to_char(snap_id),'836',ELAPSED_TIME_DELTA,0)) c_836
       , sum(decode(to_char(snap_id),'837',ELAPSED_TIME_DELTA,0)) c_837
       , sum(decode(to_char(snap_id),'838',ELAPSED_TIME_DELTA,0)) c_838
       , sum(decode(to_char(snap_id),'839',ELAPSED_TIME_DELTA,0)) c_839
       , sum(decode(to_char(snap_id),'840',ELAPSED_TIME_DELTA,0)) c_840
  from sys.dba_hist_sqlstat where snap_id >
= 834 and snap_id <= 840
  group by sql_id
  order by 2 desc) where rownum <= 9


Ecoulé : 00 :00 :00.76

select f_trsp('sql_id'
                               , 'snap_id'
                               , 'dba_hist_sqlstat'
                               , 'ELAPSED_TIME_DELTA'
                               , 'sys'
                               , 'dba_hist_snapshot'
                               , '834'
                               , '840'
                               , 9
                               , 'yes') lig from dual
/

LIG
----------------------------------------------------------------------------------------------
Select snap_id
       , sum(decode(to_char(sql_id),'6gvch1xu9ca3g',ELAPSED_TIME_DELTA,0)) c_6gvch1xu9ca3g
       , sum(decode(to_char(sql_id),'459f3z9u4fb3u',ELAPSED_TIME_DELTA,0)) c_459f3z9u4fb3u
       , sum(decode(to_char(sql_id),'7mdacxfm37utk',ELAPSED_TIME_DELTA,0)) c_7mdacxfm37utk
       , sum(decode(to_char(sql_id),'2b064ybzkwf1y',ELAPSED_TIME_DELTA,0)) c_2b064ybzkwf1y
       , sum(decode(to_char(sql_id),'70vs1d7ywk5m0',ELAPSED_TIME_DELTA,0)) c_70vs1d7ywk5m0
       , sum(decode(to_char(sql_id),'a8j39qb13tqkr',ELAPSED_TIME_DELTA,0)) c_a8j39qb13tqkr
       , sum(decode(to_char(sql_id),'3am9cfkvx7gq1',ELAPSED_TIME_DELTA,0)) c_3am9cfkvx7gq1
       , sum(decode(to_char(sql_id),'6v7n0y2bq89n8',ELAPSED_TIME_DELTA,0)) c_6v7n0y2bq89n8
       , sum(decode(to_char(sql_id),'2whm2vvjb98k7',ELAPSED_TIME_DELTA,0)) c_2whm2vvjb98k7
  from sys.dba_hist_sqlstat where snap_id >
= 834 and snap_id <= 840
  group by snap_id
  order by 1 asc


Ecoulé : 00 :00 :00.62

Les requêtes sont bien générées … Alors on teste avec flush et autotrace …

select * from (Select sql_id
    , sum(ELAPSED_TIME_DELTA) total
       , sum(decode(to_char(snap_id),'834',ELAPSED_TIME_DELTA,0)) c_834
       , sum(decode(to_char(snap_id),'835',ELAPSED_TIME_DELTA,0)) c_835
       , sum(decode(to_char(snap_id),'836',ELAPSED_TIME_DELTA,0)) c_836
       , sum(decode(to_char(snap_id),'837',ELAPSED_TIME_DELTA,0)) c_837
       , sum(decode(to_char(snap_id),'838',ELAPSED_TIME_DELTA,0)) c_838
       , sum(decode(to_char(snap_id),'839',ELAPSED_TIME_DELTA,0)) c_839
       , sum(decode(to_char(snap_id),'840',ELAPSED_TIME_DELTA,0)) c_840
  from sys.dba_hist_sqlstat where snap_id >
= 834 and snap_id <= 840
  group by sql_id
  order by 2 desc) where rownum <= 9
/

SQL_ID             TOTAL      C_834      C_835      C_836      C_837      C_838      C_839      C_840
------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
6gvch1xu9ca3g   42391392   13013927    5392808    5393650    4953967    3261169    4969584    5406287
459f3z9u4fb3u   23436549   13594062    1852118    2019437    1463763    1243657    1659715    1603797
7mdacxfm37utk   17700940    3612265    2736617    2562545    2386164    1282571    2257281    2863497
2b064ybzkwf1y    7346698    1214429    1087398    1132594    1069030     781162    1021148    1040937
70vs1d7ywk5m0    5545662    5545662          0          0          0          0          0          0
a8j39qb13tqkr    5468028    2037413     585054     672582     653276     451146     484936     583621
3am9cfkvx7gq1    5258369     906223     765851     693923     830057     601760     762278     698277
6v7n0y2bq89n8    4980042     970111     742508     774718     700769     417384     657582     716970
2whm2vvjb98k7    4015376    4015376          0          0          0          0          0          0

9 ligne(s) sélectionnée(s).

Ecoulé : 00 :00 :00.65


Plan hash value: 950671510

--------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name          | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |               |     1 |   112 |    54   (6)| 00:00:01 |       |       |
|*  1 |  COUNT STOPKEY               |               |       |       |            |          |       |       |
|   2 |   VIEW                       |               |     1 |   112 |    54   (6)| 00:00:01 |       |       |
|*  3 |    SORT ORDER BY STOPKEY     |               |     1 |    49 |    54   (6)| 00:00:01 |       |       |
|   4 |     HASH GROUP BY            |               |     1 |    49 |    54   (6)| 00:00:01 |       |       |
|*  5 |      HASH JOIN               |               |   105 |  5145 |    52   (2)| 00:00:01 |       |       |
|   6 |       PART JOIN FILTER CREATE| :BF0000       |     8 |   128 |     3   (0)| 00:00:01 |       |       |
|*  7 |        TABLE ACCESS FULL     | WRM$_SNAPSHOT |     8 |   128 |     3   (0)| 00:00:01 |       |       |
|   8 |       PARTITION RANGE AND    |               |   612 | 20196 |    48   (0)| 00:00:01 |KEY(AP)|KEY(AP)|
|*  9 |        TABLE ACCESS FULL     | WRH$_SQLSTAT  |   612 | 20196 |    48   (0)| 00:00:01 |KEY(AP)|KEY(AP)|
--------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM<=9)
   3 - filter(ROWNUM<=9)
   5 - access("SN"."DBID"="SQL"."DBID" AND "SN"."SNAP_ID"="SQL"."SNAP_ID" AND
              "SN"."INSTANCE_NUMBER"="SQL"."INSTANCE_NUMBER")
   7 - filter("SN"."SNAP_ID">
=834 AND "SN"."SNAP_ID"<=840 AND "SN"."STATUS"=0)
   9 - filter("SQL"."SNAP_ID">
=834 AND "SQL"."SNAP_ID"<=840)


Statistiques
----------------------------------------------------------
       4049  recursive calls
          4  db block gets
       1018  consistent gets
        274  physical reads
          0  redo size
       1463  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         85  sorts (memory)
          0  sorts (disk)
          9  rows processed


Select snap_id
       , sum(decode(to_char(sql_id),'6gvch1xu9ca3g',ELAPSED_TIME_DELTA,0)) c_6gvch1xu9ca3g
       , sum(decode(to_char(sql_id),'459f3z9u4fb3u',ELAPSED_TIME_DELTA,0)) c_459f3z9u4fb3u
       , sum(decode(to_char(sql_id),'7mdacxfm37utk',ELAPSED_TIME_DELTA,0)) c_7mdacxfm37utk
       , sum(decode(to_char(sql_id),'2b064ybzkwf1y',ELAPSED_TIME_DELTA,0)) c_2b064ybzkwf1y
       , sum(decode(to_char(sql_id),'70vs1d7ywk5m0',ELAPSED_TIME_DELTA,0)) c_70vs1d7ywk5m0
       , sum(decode(to_char(sql_id),'a8j39qb13tqkr',ELAPSED_TIME_DELTA,0)) c_a8j39qb13tqkr
       , sum(decode(to_char(sql_id),'3am9cfkvx7gq1',ELAPSED_TIME_DELTA,0)) c_3am9cfkvx7gq1
       , sum(decode(to_char(sql_id),'6v7n0y2bq89n8',ELAPSED_TIME_DELTA,0)) c_6v7n0y2bq89n8
       , sum(decode(to_char(sql_id),'2whm2vvjb98k7',ELAPSED_TIME_DELTA,0)) c_2whm2vvjb98k7
  from sys.dba_hist_sqlstat where snap_id >
= 834 and snap_id <= 840
  group by snap_id
  order by 1 asc
/

   SNAP_ID C_6GVCH1XU9CA3G C_459F3Z9U4FB3U C_7MDACXFM37UTK C_2B064YBZKWF1Y C_70VS1D7YWK5M0 C_A8J39QB13TQKR C_3AM9CFKVX7GQ1 C_6V7N0Y2BQ89N8 C_2WHM2VVJB98K7
---------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- ---------------
       834        13013927        13594062         3612265         1214429         5545662         2037413          906223          970111         4015376
       835         5392808         1852118         2736617         1087398               0          585054          765851          742508               0
       836         5393650         2019437         2562545         1132594               0          672582          693923          774718               0
       837         4953967         1463763         2386164         1069030               0          653276          830057          700769               0
       838         3261169         1243657         1282571          781162               0          451146          601760          417384               0
       839         4969584         1659715         2257281         1021148               0          484936          762278          657582               0
       840         5406287         1603797         2863497         1040937               0          583621          698277          716970               0

7 ligne(s) sélectionnée(s).

Ecoulé : 00 :00 :01.07


Plan hash value: 3006464286

-----------------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name          | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |               |     1 |    49 |    53   (4)| 00:00:01 |       |    |
|   1 |  SORT GROUP BY            |               |     1 |    49 |    53   (4)| 00:00:01 |       |    |
|*  2 |   HASH JOIN               |               |   105 |  5145 |    52   (2)| 00:00:01 |       |    |
|   3 |    PART JOIN FILTER CREATE| :BF0000       |     8 |   128 |     3   (0)| 00:00:01 |       |    |
|*  4 |     TABLE ACCESS FULL     | WRM$_SNAPSHOT |     8 |   128 |     3   (0)| 00:00:01 |       |    |
|   5 |    PARTITION RANGE AND    |               |   612 | 20196 |    48   (0)| 00:00:01 |KEY(AP)|KEY(AP)|
|*  6 |     TABLE ACCESS FULL     | WRH$_SQLSTAT  |   612 | 20196 |    48   (0)| 00:00:01 |KEY(AP)|KEY(AP)|
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("SN"."DBID"="SQL"."DBID" AND "SN"."SNAP_ID"="SQL"."SNAP_ID" AND
              "SN"."INSTANCE_NUMBER"="SQL"."INSTANCE_NUMBER")
   4 - filter("SN"."SNAP_ID">
=834 AND "SN"."SNAP_ID"<=840 AND "SN"."STATUS"=0)
   6 - filter("SQL"."SNAP_ID">
=834 AND "SQL"."SNAP_ID"<=840)


Statistiques
----------------------------------------------------------
       3968  recursive calls
          0  db block gets
        991  consistent gets
        308  physical reads
          0  redo size
       1429  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         76  sorts (memory)
          0  sorts (disk)
          7  rows processed

On réécrit, on commente, on enlève les variables inutiles …

create or replace function f_trsp   ( lig   in varchar2         -- Colonne qui sera utilisee en tete de ligne 
         , col   in varchar2         -- Colonne qui sera utilisee en tete de colonne
         , ltab  in varchar2         -- Table source 
         , pivot in varchar2         -- Colonne qui sera utilisee pour remplir le tabeau (forcément numérique)
         , owner in varchar2         -- Proprietaire de la table
         , mini  in varchar2         -- Valeur minimale pour col
         , maxi  in varchar2         -- Valeur maximale pour col
         , topn  in number           -- Nombre de lig affichees ordonnes par cumul de pivot descendant
         , trsp  in varchar2 := 'No' -- Transposition de la table (lig en tete de colonne et col en tete de ligne)
         ) return clob is            -- Generation d'un ordre SQL dans un Clob
 v_col    varchar2(32767);  
 v_where  varchar2(32767);  -- clause where pour mini et maxi
 v_wtopn  varchar2(1000);   -- clause where pour topn
 v_owner  varchar2(255);    
 c1   sys_refcursor;    
 v_poreq  varchar2(32767);  
 req      clob;             
begin
 -- Gestion de la variable topn, si null on prend tout
 if topn is null
 then
   v_wtopn:=' ';
 else
   v_wtopn:=' where rownum <= '||topn;
 end if;

 -- Gestion de la variable owner, si null on prend ls schema appelant 
 if owner is null
 then 
  select sys_context('USERENV','CURRENT_SCHEMA') into v_owner from sys.dual;
 else
  v_owner:=owner;
 end if;
 
 -- Gestion des variables mini et maxi, si null on prend tout
 if ( mini is not null or maxi is not null )
 then
  v_where:=' where ';
  if mini is not null 
  then
   v_where:=v_where||col||' >= '||mini ;
   if maxi is not null
   then
       v_where:=v_where||' and '||col||' <= '||maxi ;
   end if;
  else
      v_where:=v_where||col||' <= '||maxi ;
  end if;
 end if;
 
    -- Si on ne transpose pas lig et col  
 if upper(trsp) = 'NO'
 then
   req:='select * from (Select '||lig||', sum('||pivot||') total ';
   open c1 for 'select distinct to_char('||col||') from '||v_owner||'.'||ltab||v_where||' order by 1' ;
   loop
    fetch c1 into v_col;
    exit when c1%notfound;
    v_poreq:='
   , sum(decode(to_char('||col||'),'''||v_col||''','||pivot||',0)) c_'||trim(v_col) ;
    dbms_lob.writeappend(req,length(v_poreq),v_poreq);
   end loop;
   close c1;
   v_poreq:='
from '||v_owner||'.'||ltab||v_where||'
group by '||lig||'
order by 2 desc)'||v_wtopn ;
   dbms_lob.writeappend(req,length(v_poreq),v_poreq);

    -- Si on transpose lig et col  
 else
   req:='Select '||col||' ';
   open c1 for 'select ent from (select distinct to_char('||lig||') ent, sum('||pivot||') tot from '||v_owner||'.'||ltab||v_where||' group by to_char('||lig||') order by 2 desc)'||v_wtopn ;
   loop
     fetch c1 into v_col;
     exit when c1%notfound;
     v_poreq:='
   , sum(decode(to_char('||lig||'),'''||v_col||''','||pivot||',0)) c_'||trim(v_col) ;
     dbms_lob.writeappend(req,length(v_poreq),v_poreq);
   end loop;
   close c1;
   v_poreq:='
from '||v_owner||'.'||ltab||v_where||'
group by '||col||'
order by 1 asc';
   dbms_lob.writeappend(req,length(v_poreq),v_poreq);
 end if;        
 
 return req;
end f_trsp;
/

Bon c’est mignon, mais on a un nombre de colonnes variables et non défini en résultat, du coup c’est pas forcément simple à utiliser dans un code PL/SQL … A suivre

Stored outlines

Pour voir quelle catégorie d’outlines est utilisée au niveau instance il suffit de suivre La note metalink (internal only, you don’t have access) ou de retrouver ses diverses transcription oveurzeunaitte comme par exemple sur le site Oracle Guy ou le blog El Caro histoire de rester sur Blogger.

Donc que est l’intérêt de ce post ??? Le français de France allons, pour les dba qui se vrillent l’œil dès que la Perfide Albion vient envahir leur champ de vision

Alors comment qu’on fait Madame ?

Simplissime, on sort le code hexa d’une partie de la SGA (ou de l’UGA si des fois on fait ça au niveau session) L’enfance de l’art non ?

Les Instructions qui vont bien :

SQL> oradebug setmypid
SQL> oradebug dumpvar sga sgauso

les résultats sont pour le moins cocaces

qolprm sgauso_ [20014F44, 20014F68) = 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000

Qui signifie : “FALSE” … Oui c’est pas forcément évident

qolprm sgauso_ [8001304C, 80013070) = 00000001 00074445 4641554C 54000000 ... 

Qui signifie : “DEFAULT” … Sachant que DEFAULT fait 7 lettres et qu’en hexadécimal DEFAULT s’écrit 4445 4641554c 54, on commence à comprendre, on la refait doucement :

qolprm sgauso_ [8001304C, 80013070) = 00000001 00074445 4641554C 54000000 ... 
7 D E F A U L T

OK ?

Rendre dbms_metadata lisible

DBMS_METADATA lisible, comment faire ?

Le package dbms_metadata existe depuis la version 9i et permet de regénerer les commandes DDL relatives aux objets ORACLE … Maintenant, pour l’utilisation et la liste des focntion au choix : RTFM ou RTFDB (DB pour Donald B le bien connu copiste en chef de la documentation Oracle) ce qui, au final revient à peu près au même. Cependant, souvent sa sortie contient des informations dont on se passerait bien, comme la gestion des extents ou autres alors qu’on utilise la plupart du temps les paramètres par défaut du tablespace.

Deux trois petites astuces bien sympa à retenir

  • Ne jamais faire un dbms_metadata sur une liste d’objets (si un objet plante, les suivants ne sont pas traités)
  • Utiliser long 9999999 et longc 9999999
  • Utiliser des lignes larges (300 à 400 caractères, on peut aller jusqu’à 32768 soit 32k) et un “trimspool on” s’il on choisit de rediriger la sortie dans un fichier.
  • Demander la mise en forme avec les terminaisons SQL ( / ou ; )
    exec dbms_metadata.set_transform_param( DBMS_METADATA.SESSION_TRANSFORM, 'SQLTERMINATOR', TRUE ); 
  • Eviter les paramètres de stockage afin d’utiliser ceux du tablespace :
    exec dbms_metadata.set_transform_param( DBMS_METADATA.SESSION_TRANSFORM, 'STORAGE', false); 

L’exemple suivant donne le DDL de la table WRH$_SQL_PLAN dans ses deux versions

Comportement par défaut de DBMS_METADATA

select dbms_metadata.get_ddl('TABLE','WRH$_SQL_PLAN',USER) from sys.dual ;
DBMS_METADATA.GET_DDL('TABLE','WRH$_SQL_PLAN',USER)
--------------------------------------------------------------------------

  CREATE TABLE "SYS"."WRH$_SQL_PLAN"
   (    "SNAP_ID" NUMBER,
        "DBID" NUMBER NOT NULL ENABLE,

        [...]
        
        "OTHER_XML" CLOB,
         CONSTRAINT "WRH$_SQL_PLAN_PK" PRIMARY KEY ("DBID", "SQL_ID", "PLAN_HASH_VALUE", "ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "SYSAUX"  ENABLE
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "SYSAUX"
 LOB ("OTHER_XML") STORE AS BASICFILE (
  TABLESPACE "SYSAUX" ENABLE STORAGE IN ROW CHUNK 8192 RETENTION
  NOCACHE LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT))

DBMS_METADATA lisible, ou du moins beaucoup moins inutilement chargé

Paramétrage

exec dbms_metadata.set_transform_param( DBMS_METADATA.SESSION_TRANSFORM, 'SQLTERMINATOR', TRUE );

PL/SQL procedure successfully completed.

exec dbms_metadata.set_transform_param( DBMS_METADATA.SESSION_TRANSFORM, 'STORAGE', false);

PL/SQL procedure successfully completed.

sortie DBMS_METADATA lisible

select dbms_metadata.get_ddl('TABLE','WRH$_SQL_PLAN',USER) from sys.dual ;
DBMS_METADATA.GET_DDL('TABLE','WRH$_SQL_PLAN',USER)
--------------------------------------------------------------------------

  CREATE TABLE "SYS"."WRH$_SQL_PLAN"
   (    "SNAP_ID" NUMBER,
        "DBID" NUMBER NOT NULL ENABLE,

        [...]

        "OTHER_XML" CLOB,
         CONSTRAINT "WRH$_SQL_PLAN_PK" PRIMARY KEY ("DBID", "SQL_ID", "PLAN_HASH_VALUE", "ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
  TABLESPACE "SYSAUX"  ENABLE
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
 NOCOMPRESS LOGGING
  TABLESPACE "SYSAUX"
 LOB ("OTHER_XML") STORE AS BASICFILE (
  TABLESPACE "SYSAUX" ENABLE STORAGE IN ROW CHUNK 8192 RETENTION
  NOCACHE LOGGING ) ;

Comptons les logs (redo ou arch) générés

Je l’oublie toujours et pourtant je l’utilise aussi souvent que possible : Le script qui donne le nombre de logs par heures …

SET VERIFY OFF

COLUMN H00 FORMAT 999 HEADING '00'
COLUMN H01 FORMAT 999 HEADING '01'
COLUMN H02 FORMAT 999 HEADING '02'
COLUMN H03 FORMAT 999 HEADING '03'
COLUMN H04 FORMAT 999 HEADING '04'
COLUMN H05 FORMAT 999 HEADING '05'
COLUMN H06 FORMAT 999 HEADING '06'
COLUMN H07 FORMAT 999 HEADING '07'
COLUMN H08 FORMAT 999 HEADING '08'
COLUMN H09 FORMAT 999 HEADING '09'
COLUMN H10 FORMAT 999 HEADING '10'
COLUMN H11 FORMAT 999 HEADING '11'
COLUMN H12 FORMAT 999 HEADING '12'
COLUMN H13 FORMAT 999 HEADING '13'
COLUMN H14 FORMAT 999 HEADING '14'
COLUMN H15 FORMAT 999 HEADING '15'
COLUMN H16 FORMAT 999 HEADING '16'
COLUMN H17 FORMAT 999 HEADING '17'
COLUMN H18 FORMAT 999 HEADING '18'
COLUMN H19 FORMAT 999 HEADING '19'
COLUMN H20 FORMAT 999 HEADING '20'
COLUMN H21 FORMAT 999 HEADING '21'
COLUMN H22 FORMAT 999 HEADING '22'
COLUMN H23 FORMAT 999 HEADING '23'
COLUMN TOTAL FORMAT 999,999 HEADING 'Total'

SELECT
TO_CHAR(first_time, 'MM/DD') DAY
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'00',1,0)) H00
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'01',1,0)) H01
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'02',1,0)) H02
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'03',1,0)) H03
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'04',1,0)) H04
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'05',1,0)) H05
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'06',1,0)) H06
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'07',1,0)) H07
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'08',1,0)) H08
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'09',1,0)) H09
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'10',1,0)) H10
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'11',1,0)) H11
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'12',1,0)) H12
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'13',1,0)) H13
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'14',1,0)) H14
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'15',1,0)) H15
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'16',1,0)) H16
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'17',1,0)) H17
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'18',1,0)) H18
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'19',1,0)) H19
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'20',1,0)) H20
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'21',1,0)) H21
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'22',1,0)) H22
, SUM(DECODE(TO_CHAR(first_time, 'HH24'),'23',1,0)) H23
, COUNT(*) TOTAL
FROM
v$log_history a
GROUP BY TO_CHAR(first_time, 'MM/DD')
/

Ce qui donne le fameux :

DAY     00   01   02   03   04   05   06   07   08   09   10   11   12   13   14   15   16   17   18   19   20   21   22   23    Total
----- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --------
08/14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 12 16
08/15 12 13 14 13 9 14 13 12 10 15 9 14 15 7 17 5 17 7 15 8 16 4 19 5 283
08/16 15 8 6 17 5 17 9 11 11 18 12 2 17 15 10 3 14 20 2 13 0 20 17 1 263
08/17 11 0 20 19 3 10 1 21 18 2 12 0 12 18 13 4 11 0 16 23 8 0 0 0 222
08/28 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1
09/09 0 0 0 0 0 0 0 0 0 0 4 1 0 0 36 15 2 0 0 0 0 0 13 13 84
09/10 2 0 16 2 16 11 1 0 17 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 69
10/16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 25 2 8 11 50
10/17 6 0 5 8 4 6 10 1 0 1 2 1 2 13 12 0 1 6 7 7 1 0 2 2 97
10/18 3 3 3 5 18 2 4 7 5 9 1 2 2 3 3 3 6 1 18 1 0 0 0 0 99
10/23 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
10/27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1
10/28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 11 10 0 0 0 0 24
10/29 0 0 0 0 0 0 0 0 0 4 18 7 0 3 5 5 6 6 4 3 3 5 4 15 88
10/30 1 1 5 5 6 3 4 7 3 3 2 8 0 17 0 0 0 0 0 0 0 0 0 0 65

Bien pratique pour tailler les logs

Paramètres internes de l’instance

La table x$kvit donne une liste de paramètres dynamiques utilisés au moment du select.

La version 10g remonte la liste suivante :

select KVITTAG
, KVITDSC
, KVITVAL
from x$kvit
/

KVITTAG KVITDSC KVITVAL
-------------------- ---------------------------------------------------------------- ----------
ksbcpu number of logical CPUs in the system used by Oracle 2
ksbcpucore number of physical CPU cores in the system used by Oracle 0
ksbcpusocket number of physical CPU sockets in the system used by Oracle 0
ksbcpu_hwm high water mark of number of CPUs used by Oracle 2
ksbcpucore_hwm high water mark of number of CPU cores on system 0
ksbcpusocket_hwm high water mark of number of CPU sockets on system 0
ksbcpu_actual number of available CPUs in the system 2
ksbcpu_dr CPU dynamic reconfiguration supported 1
kcbnbh number of buffers 136551
kcbldq large dirty queue if kcbclw reaches this 25
kcbfsp Max percentage of LRU list foreground can scan for free 40
kcbcln Initial percentage of LRU list to keep clean 2
kcbnbf number buffer objects 750
kcbwst Flag that indicates recovery or db suspension 0
kcteln Error Log Number for thread open 0
kcvgcw SGA: opcode for checkpoint cross-instance call 0
kcvgcw SGA:opcode for pq checkpoint cross-instance call 0

La version 9i remonte la liste suivante :


KVITTAG KVITDSC KVITVAL
-------------------- ---------------------------------------------------------------- ----------
kcbnbh number of buffers 381120
kcbldq large dirty queue if kcbclw reaches this 25
kcbdsp Max percentage of LRU list dbwriter can scan for dirty 25
kcbfsp Max percentage of LRU list foreground can scan for free 40
kcbnbf number buffer objects 5000
kcbwst Flag that indicates recovery or db suspension 0
kcteln Error Log Number for thread open 0
kcvgcw SGA: opcode for checkpoint cross-instance call 0

Incidence du statistics level

La variable statistics level permet des stocker des statistiques dans certaines vues.

La requête suivante permet de voir quelle vue est impactée, à quel niveau de statistique et si le calcul de la statistique peut être modifié au niveau session

select decode(ACTIVATION_LEVEL,1,'Typical','All') "A Level"
, Name
, decode(SESSION_CHANGEABLE,1,'Ok',' -') "Modif"
, decode(SESSION_STATUS,1,'On',' -') "Sess"
, decode(SYSTEM_STATUS,1,'On',' -') "Sys"
, VIEW_NAME
from x$prmsltyx
order by ACTIVATION_LEVEL
/

En 11gR2 on obtient

A Level NAME                                                             Mo Se Sy VIEW_NAME
------- ---------------------------------------------------------------- -- -- -- -------------------------
Typical Buffer Cache Advice - On On V$DB_CACHE_ADVICE
Typical V$IOSTAT_* statistics - On On
Typical Timed Statistics Ok On On
Typical MTTR Advice - On On V$MTTR_TARGET_ADVICE
Typical Adaptive Thresholds Enabled - On On
Typical Segment Level Statistics - On On V$SEGSTAT
Typical PGA Advice - On On V$PGA_TARGET_ADVICE
Typical Shared Pool Advice - On On V$SHARED_POOL_ADVICE
Typical Modification Monitoring - On On
Typical Longops Statistics - On On V$SESSION_LONGOPS
Typical Bind Data Capture - On On V$SQL_BIND_CAPTURE
Typical Ultrafast Latch Statistics - On On
Typical Threshold-based Alerts - On On
Typical Global Cache Statistics - On On
Typical Active Session History - On On V$ACTIVE_SESSION_HISTORY
Typical Undo Advisor, Alerts and Fast Ramp up - On On V$UNDOSTAT
Typical Streams Pool Advice - On On V$STREAMS_POOL_ADVICE
Typical Time Model Events Ok On On V$SESS_TIME_MODEL
Typical Plan Execution Sampling Ok On On V$ACTIVE_SESSION_HISTORY
Typical Automated Maintenance Tasks - On On
Typical SQL Monitoring Ok On On V$SQL_MONITORING
All Plan Execution Statistics Ok - - V$SQL_PLAN_STATISTICS
All Timed OS Statistics Ok - -

En 10g on obtient

A Level NAME                                     Mo Se Sy VIEW_NAME
------- ---------------------------------------- -- -- -- ------------------------------
Typical Buffer Cache Advice - On On V$DB_CACHE_ADVICE
Typical Streams Pool Advice - On On V$STREAMS_POOL_ADVICE
Typical Timed Statistics Ok On On
Typical MTTR Advice - On On V$MTTR_TARGET_ADVICE
Typical Undo Advisor, Alerts and Fast Ramp up - On On V$UNDOSTAT
Typical Segment Level Statistics - On On V$SEGSTAT
Typical PGA Advice - On On V$PGA_TARGET_ADVICE
Typical Shared Pool Advice - On On V$SHARED_POOL_ADVICE
Typical Modification Monitoring - On On
Typical Longops Statistics - On On V$SESSION_LONGOPS
Typical Bind Data Capture - On On V$SQL_BIND_CAPTURE
Typical Ultrafast Latch Statistics - On On
Typical Threshold-based Alerts - On On
Typical Global Cache Statistics - On On
Typical Active Session History - On On V$ACTIVE_SESSION_HISTORY
All Plan Execution Statistics Ok - - V$SQL_PLAN_STATISTICS
All Timed OS Statistics Ok - -

En 9i on obtient

A Level NAME                           Mo Se Sy VIEW_NAME
------- ------------------------------ -- -- -- ------------------------------
Typical Buffer Cache Advice - On On V$DB_CACHE_ADVICE
Typical MTTR Advice - On On V$MTTR_TARGET_ADVICE
Typical Timed Statistics Ok On On
Typical PGA Advice - On On V$PGA_TARGET_ADVICE
Typical Shared Pool Advice - On On V$SHARED_POOL_ADVICE
Typical Segment Level Statistics - On On V$SEGSTAT
All Timed OS Statistics Ok - -
All Plan Execution Statistics Ok - - V$SQL_PLAN_STATISTICS

Lister les tables X$ d’une version Oracle

Ce petit script PERL permet de lister les tables “X$” du moteur … Ce qui ne dit pas ce qu’elles font

#!/usr/bin/perl -w

use strict;

#open O, ($ORACLE_HOME."/bin/oracle");
open O, ("/logiciels/oracle/ora_10.2.0/bin/oracle");
open F, (">x");

my $l;
my $p = ' ' x 40;
my %x;

while (read (O,$l,10000)) {
$l = $p.$l;

foreach ($l =~ /(x$w{3,})/g) {
$x{$_}++;
}

$p = substr ($l,-40);
}

foreach (sort keys %x) {
print F "$_n";
}

Ce script provient du site http://www.adp-gmbh.ch

On peut trouver certaines explications sur le contenu des tables “X$” sur freeshell, sur le site BC Oracle Training ou en bien d’autres endroits 😉

La version 10 d’oracle en liste quelques 350 par exemple la table x$kwddef liste l’ensemble des mots clés du SQL, la requête suivante permet donc de les lister :

select KEYWORD from x$kwddef
order by KEYWORD
/

Générer des rapports statspack

Petite procédure pour générer un nombre important de rapports statspack.

SET SERVEROUTPUT OFF
set feedback off

var delta number;
var date_fin varchar2(20);
begin
:delta:=&1;
--  :date_fin:=&2;
end;
/

create or replace procedure dba_all_reports( delta in number
                                         , fin in varchar default to_char(sysdate, 'YYYYMMDDHH24MI') ) as
begin
for i in (select snap_id
        , snap_next
        , to_char(snap_time, 'MMDD_HH24MI') rpt_name
        , instance_name
      from (select s.SNAP_ID
              , lead(s.SNAP_ID,1) over (order by s.SNAP_ID) SNAP_NEXT
              , s.STARTUP_TIME
              , lead(s.STARTUP_TIME, 1) over (order by s.SNAP_ID) STARTUP_NEXT
              , s.SNAP_TIME
              , i.INSTANCE_NAME
            from stats$snapshot s, v$instance i)
      where SNAP_NEXT=SNAP_ID+1
      and STARTUP_TIME=STARTUP_NEXT
      and snap_time between to_date(fin,'YYYYMMDDHH24MI')-delta and to_date(fin,'YYYYMMDDHH24MI') )
loop
DBMS_OUTPUT.ENABLE (32000);
dbms_output.put_line ('define begin_snap='||i.snap_id);
dbms_output.put_line ('define end_snap='||i.snap_next);
dbms_output.put_line ('define report_name='||i.instance_name||'_'||i.rpt_name);
dbms_output.put_line ('@?/rdbms/admin/spreport.sql');
end loop;
end;
/

SET SERVEROUTPUT ON
spool reports.sql
-- execute dba_all_reports(:delta, :date_fin)
execute dba_all_reports(:delta)
spool off

@reports.sql

drop procedure dba_all_reports;

Les rapports ainsi générés sont nommés <ORACLE_SID>_<MOIS><JOUR>_<HEURE><MINUTES>.lst

On peut décommenter les lignes

--  :date_fin:=&2;

et

-- execute dba_all_reports(:delta, :date_fin)

et supprimer la ligne

execute

dba_all_reports(:delta)

de manière à générer des rapports anciens.

Les requêtes consommatrices

Bon un petit peu de technique histoire d’aider ma mémoire avec une requête bien sympatoche histoire de tracer la consommation SQL d’une base


select sql_id, dsk_rd, tot_rd, to_char((dsk_rd/tot_rd)*100,'999D99') || ' %' "PCT RDS"
from (select distinct 1 c1
, sql_id
, sum(disk_reads_DELTA) over (partition by sql_id) dsk_rd
, sum(disk_reads_delta) over (partition by 1) tot_rd
from DBA_HIST_SQLSTAT
where nvl(disk_reads_DELTA,0) > 0
order by 3 desc)
where rownum < 11
and tot_rd > 0

Au résultat on obtient un petit tableau de ce type


SQL_ID DSK_RD TOT_RD PCT RDS
------------- ---------- ---------- ---------
b7jn4mf49n569 6234 21773 28,63 %
gfjvxb25b773h 1990 21773 9,14 %
c7sn076yz7030 939 21773 4,31 %
cvn54b7yz0s8u 849 21773 3,90 %
cfz686a6qp0kg 808 21773 3,71 %
c2p32r5mzv8hb 709 21773 3,26 %
7q2wnyku8mfv0 630 21773 2,89 %
b3ys9bs1v9cch 547 21773 2,51 %
39m4sx9k63ba2 437 21773 2,01 %
02577v815yp77 412 21773 1,89 %

Bien cryptique ? Certes mais utile dans le cadre d’un audit de perf. (à partir de la version 10 d’oracle).

Pour tracer une évolution au travers des snapshots AWR (Automatic Workload Reppository) on pourra modifier de cette manière :


break on snap_id skip 1

select SNAP_ID
, SQL_ID
, disk_reads_DELTA
, to_char((disk_reads_delta/tot)*100,'999D99') || ' %' "PCT RDS"
from (select SNAP_ID
, SQL_ID
, disk_reads_DELTA
, dense_rank() over ( partition by snap_id order by disk_reads_DELTA desc, sql_id asc) rk
, sum(disk_reads_DELTA) over ( partition by snap_id) tot
from DBA_HIST_SQLSTAT
where nvl(disk_reads_DELTA,0) > 0)
where rk < 11
and tot > 0
order by snap_id, rk

La remontée est alors du type :


SNAP_ID SQL_ID DISK_READS_DELTA PCT RDS
---------- ------------- ---------------- ---------
273 b3ys9bs1v9cch 18 42,86 %
4ng4xn7jw3dbb 8 19,05 %
0a1k5zuh04f38 7 16,67 %
96g93hntrzjtr 3 7,14 %
bunssq950snhf 2 4,76 %
as3uq6ggb3gx6 1 2,38 %
f4fvybbfcbk5y 1 2,38 %
gc7b0drtzbyc6 1 2,38 %
8sd37ztuwkyyc 1 2,38 %

276 dnwpm0gdccrph 6 100,00 %

SNAP_ID SQL_ID DISK_READS_DELTA PCT RDS
---------- ------------- ---------------- ---------

287 g337099aatnuj 1 100,00 %

289 8yh7x8qkgswu2 2 100,00 %

297 b3ys9bs1v9cch 16 42,11 %
5rxbazwmcdfaz 7 18,42 %
70vs1d7ywk5m0 7 18,42 %
4ng4xn7jw3dbb 3 7,89 %
96g93hntrzjtr 3 7,89 %
as3uq6ggb3gx6 1 2,63 %

SNAP_ID SQL_ID DISK_READS_DELTA PCT RDS
---------- ------------- ---------------- ---------
297 gc7b0drtzbyc6 1 2,63 %