As we all known, some developers/DBAs have bad habit to "tune" SQL's by adding new indexes with or without real need. So any unnecessary index, beside professional aspect, has 2 major weakness:
- Adding new index very often change current SQL plans (what, as Murphy says, mostly finish badly!)
- Set overhead on I/O and CPU in any DML operation on involved table (maintain index blocks)
select table_name, index_name, monitoring, used from v$object_usage where table_name = '&&TABLE_NAME' order by table_name, index_name;This solution has two disadvantages:
- Regardless how many times index was used it is recorded just once
- There is no other way to see any additional info on index usage
The solution
To get a solution for this, there is one pre requirement-AWR. AWR (Automatic Workload Repository), which is part of "Tuning and Diagnostic Packs", must be installed, what require additional licensing. AWR is snapshot repository of important database activity, so AWR should able to keep data with retention of minimum of 30 days (46 days are perfect) and those snapshots must be taken in appropriate interval. Here is easy example how to make that (as I do):BEGIN DBMS_WORKLOAD_REPOSITORY.modify_snapshot_settings( retention => 66240, -- = 46 Days interval => 15) -- = 15 Minutes ; END; /Now you need to wait 30/46 days and then you can query index usage.
After that you are able to use this script that shows all indexes in schema (defined with first parameter-&&1) which are bigger then value (defined with scripts's second parameter-&&2). Remember that in mine case monitor period is 46 days, what is not so little.
/* --------------------------------------------------------------------------- CR/TR# : Purpose : Shows index usage by execution (find problematic indexes) Date : 22.01.2008. Author : Damir Vadas, damir.vadas@gmail.com Remarks : run as privileged user Must have AWR run because sql joins data from there works on 10g > @index_usage SCHEMA MIN_INDEX_SIZE Changes (DD.MM.YYYY, Name, CR/TR#): 25.11.2010, Damir Vadas added index size as parameter 30.11.2010, Damir Vadas fixed bug in query --------------------------------------------------------------------------- */ set linesize 140 set pagesize 160 clear breaks clear computes break on TABLE_NAME skip 2 ON INDEX_NAME ON INDEX_TYPE ON MB compute sum of NR_EXEC on TABLE_NAME SKIP 2 compute sum of MB on TABLE_NAME SKIP 2 SET TIMI OFF set linesize 140 set pagesize 10000 col OWNER noprint col TABLE_NAME for a30 heading 'Table name' col INDEX_NAME for a25 heading 'Index name' col INDEX_TYPE for a10 heading 'Index type' col INDEX_OPERATION for a21 Heading 'Index operation' col NR_EXEC for 9G999G990 heading 'Executions' col MB for 999G990D90 Heading 'Index|Size MB' justify right WITH Q AS ( SELECT S.OWNER A_OWNER, TABLE_NAME A_TABLE_NAME, INDEX_NAME A_INDEX_NAME, INDEX_TYPE A_INDEX_TYPE, SUM(S.bytes) / 1048576 A_MB FROM DBA_SEGMENTS S, DBA_INDEXES I WHERE S.OWNER = '&&1' AND I.OWNER = '&&1' AND INDEX_NAME = SEGMENT_NAME GROUP BY S.OWNER, TABLE_NAME, INDEX_NAME, INDEX_TYPE HAVING SUM(S.BYTES) > 1048576 * &&2 ) SELECT /*+ NO_QUERY_TRANSFORMATION(S) */ A_OWNER OWNER, A_TABLE_NAME TABLE_NAME, A_INDEX_NAME INDEX_NAME, A_INDEX_TYPE INDEX_TYPE, A_MB MB, DECODE (OPTIONS, null, ' -',OPTIONS) INDEX_OPERATION, COUNT(OPERATION) NR_EXEC FROM Q, DBA_HIST_SQL_PLAN d WHERE D.OBJECT_OWNER(+)= q.A_OWNER AND D.OBJECT_NAME(+) = q.A_INDEX_NAME GROUP BY A_OWNER, A_TABLE_NAME, A_INDEX_NAME, A_INDEX_TYPE, A_MB, DECODE (OPTIONS, null, ' -',OPTIONS) ORDER BY A_OWNER, A_TABLE_NAME, A_INDEX_NAME, A_INDEX_TYPE, A_MB DESC, NR_EXEC DESC ; PROMPT Showed only indexes in &&1 schema whose size > &&2 MB in period: SET HEAD OFF; select to_char (min(BEGIN_INTERVAL_TIME), 'DD.MM.YYYY') || '-' || to_char (max(END_INTERVAL_TIME), 'DD.MM.YYYY') from dba_hist_snapshot; SET HEAD ON SET TIMI ONScript is run against "SOME_SCHEMA" and query indexes bigger then 100 MB:
SQL> @index_usage SOME_SCHEMA 100Here is reduced script's output on one of database which I was dealing with. Highlighted are some of indexes with special interest:
Index Table name Index name Index type Size MB Index operation Executions ------------------------------ ------------------------- ---------- ----------- --------------------- ---------- D_DOPRIX D_DOP_DAT NORMAL 208.00 RANGE SCAN 24 D_DOP_DATUM_NDX NORMAL 193.00 - 0 D_DOPX_PRIM NORMAL 168.00 FAST FULL SCAN 1 D_DOPX_R NORMAL 168.00 RANGE SCAN 3 FAST FULL SCAN 1 D_DPX_ORGJED NORMAL 176.00 - 0 D_DPX_VP_RAD NORMAL 240.00 RANGE SCAN 1 D_PK_DOPRIX NORMAL 312.00 SKIP SCAN 1 RANGE SCAN 1 ****************************** ************************* ********** ----------- ---------- sum 1,945.00 32 D_OSIGURANJE D_OSIG_OO NORMAL 208.00 FAST FULL SCAN 1 D_OSIG_OS NORMAL 424.00 RANGE SCAN 4 ****************************** ************************* ********** ----------- ---------- sum 632.00 5 G_DNEVNIK DK_BRDOK_I NORMAL 184.00 RANGE SCAN 99 DK_GOD_FK_I NORMAL 168.00 RANGE SCAN 137 FAST FULL SCAN 2 DK_KON_FK_I NORMAL 200.00 RANGE SCAN 627 FULL SCAN 10 DK_MTR_FK_I NORMAL 201.00 RANGE SCAN 254 DK_ORG_FK_I NORMAL 176.00 RANGE SCAN 109 DK_PDS_FK_I NORMAL 160.00 RANGE SCAN 36 DK_PK NORMAL 185.00 RANGE SCAN (MIN/MAX) 1 DK_VRD_FK_I NORMAL 168.00 RANGE SCAN 6 ****************************** ************************* ********** ----------- ---------- sum 1,810.00 1,281 G_PROMET_PP SCP_BROJ_URA NORMAL 112.00 RANGE SCAN 2 SCP_PK NORMAL 104.00 UNIQUE SCAN 205 RANGE SCAN (MIN/MAX) 1 SCP_SIFPP_KONTO_I NORMAL 104.00 RANGE SCAN 588 ****************************** ************************* ********** ----------- ---------- sum 424.00 796 G_PROMET_S SP_MT_FK_I NORMAL 104.00 RANGE SCAN 1 SP_PK NORMAL 136.00 RANGE SCAN (MIN/MAX) 1 SP_SCP_FK_I NORMAL 136.00 RANGE SCAN 34 FAST FULL SCAN 1 ****************************** ************************* ********** ----------- ---------- sum 512.00 37 I_DATPBZ_S002 IDS2_AUT_I NORMAL 189.00 - 0 IDS2_DATSOC_I NORMAL 136.00 RANGE SCAN 1 SAMPLE FAST FULL SCAN 1 IDS2_DATTRAN_I NORMAL 152.00 RANGE SCAN 2 IDS2_DATUM_I NORMAL 152.00 RANGE SCAN 10 FULL SCAN 1 IDS2_IDS1_FK_I NORMAL 129.00 RANGE SCAN 16 IDS2_PK NORMAL 144.00 - 0 IDS2_PZNBR NORMAL 144.00 - 0 IDS2_VRSTA_PROD_I NORMAL 113.00 FAST FULL SCAN 1 ****************************** ************************* ********** ----------- ---------- sum 1,447.00 32 NC_BESPLATNI NSB_CSC_I NORMAL 112.00 RANGE SCAN 13 NSB_DATUM_I NORMAL 112.00 RANGE SCAN 26 NSB_GRUPA_I NORMAL 120.00 - 0 ****************************** ************************* ********** ----------- ---------- sum 344.00 39 NC_CASH_UP_DETAIL_T NCD_CASH_UP_I NORMAL 9,984.00 - 0 NCD_CASH_UP_IX_REDD NORMAL 4,288.00 - 0 NCD_PAIEMENT_I NORMAL 3,200.00 - 0 ****************************** ************************* ********** ----------- ---------- sum 17,472.00 0 NC_CRED_T TRC_CONTENU_I NORMAL 144.00 RANGE SCAN 29 SAMPLE FAST FULL SCAN 1 TRC_DATE_I NORMAL 104.00 RANGE SCAN 7 SAMPLE FAST FULL SCAN 1 ****************************** ************************* ********** ----------- ---------- sum 496.00 38 NC_SUBSCRIPTION_SALDO NC_SUBSCRIPTION_SALDO_IXX NORMAL 128.00 RANGE SCAN 1 NSO_CSC_I NORMAL 696.00 RANGE SCAN 3 NSO_DATUM_I NORMAL 568.00 RANGE SCAN 11 NSO_DATUM2_I NORMAL 569.00 RANGE SCAN 5 NSO_GRUPA_I NORMAL 712.00 RANGE SCAN 136 FAST FULL SCAN 1 NSO_IND_I NORMAL 368.00 RANGE SCAN 14 FAST FULL SCAN 3 NSO_PAR_I NORMAL 488.00 RANGE SCAN 2,569 FAST FULL SCAN 2 NSO_PK NORMAL 432.00 UNIQUE SCAN 5 NSO_PROD_I NORMAL 432.00 - 0 NSO_I NORMAL 560.00 FULL SCAN 1 ****************************** ************************* ********** ----------- ---------- sum 6,521.00 2,751 NC_TRANSACTION_ARZ_T TRA_I NORMAL 184.00 FAST FULL SCAN 1 FULL SCAN 1 SAMPLE FAST FULL SCAN 1 TRA_ID_PAIEMENT_I NORMAL 136.00 RANGE SCAN 1 SAMPLE FAST FULL SCAN 1 TRA_ID_VOIE_I NORMAL 128.00 SAMPLE FAST FULL SCAN 1 TRA_ISSUER_I NORMAL 105.00 RANGE SCAN 3 SAMPLE FAST FULL SCAN 1 TRA_NUM_TR_I NORMAL 112.00 RANGE SCAN 13 SAMPLE FAST FULL SCAN 1 TRA_RESEAU_I NORMAL 160.00 RANGE SCAN 13 SAMPLE FAST FULL SCAN 1 TR_ARZ_BROJ_CSC_I NORMAL 145.00 RANGE SCAN 5 SAMPLE FAST FULL SCAN 1 TR_ARZ_DATUM_IZLAZA_I NORMAL 168.00 RANGE SCAN 1,037 FULL SCAN 4 SAMPLE FAST FULL SCAN 1 TR_ARZ_DATUM_ULAZA_I NORMAL 153.00 RANGE SCAN 447 FULL SCAN 1 SAMPLE FAST FULL SCAN 1 TR_ARZ_KLASA_I NORMAL 128.00 SAMPLE FAST FULL SCAN 1 TRA_TICK_IDX NORMAL 128.00 RANGE SCAN 408 SAMPLE FAST FULL SCAN 1 ****************************** ************************* ********** ----------- ---------- sum 3,343.00 1,945 NC_TRANSACTION_MODE7_T TRM7_BROJ_CSC_I NORMAL 193.00 RANGE SCAN 2 SAMPLE FAST FULL SCAN 1 TRM7_CLASSE_I NORMAL 152.00 SAMPLE FAST FULL SCAN 1 TRM7_DATE_I NORMAL 169.00 RANGE SCAN 2 SAMPLE FAST FULL SCAN 1 TRM7_DATE2_I NORMAL 184.00 RANGE SCAN 65 SAMPLE FAST FULL SCAN 1 TRM7_GP_I NORMAL 199.00 RANGE SCAN 1 SAMPLE FAST FULL SCAN 1 TRM7_LENGTH_I FUNCTION-B 129.00 SAMPLE FAST FULL SCAN 1 ASED NORMA L TRM7_OBS_MP_I NORMAL 152.00 RANGE SCAN 5 SAMPLE FAST FULL SCAN 1 TRM7_OBS_SEQ_I NORMAL 145.00 SAMPLE FAST FULL SCAN 1 TRM7_PAIEMENT_I NORMAL 152.00 SAMPLE FAST FULL SCAN 1 TRM7_RESEAU_I NORMAL 193.00 RANGE SCAN 5 SAMPLE FAST FULL SCAN 1 TRM7_TAB_CLASSE_I NORMAL 152.00 SAMPLE FAST FULL SCAN 1 TRM7_TAB_PLAZA_I NORMAL 192.00 SAMPLE FAST FULL SCAN 2 ****************************** ************************* ********** ----------- ---------- sum 3,102.00 93 NC_TRANSACTION_OK_T TROK_DATE_I NORMAL 728.00 RANGE SCAN 20 TROK_I NORMAL 536.00 - 0 TROK_ID_VOIE_I NORMAL 608.00 - 0 TROK_KLASA_I NORMAL 632.00 - 0 TROK_OP_DATE_I NORMAL 400.00 - 0 TROK_PAIEMENT_I NORMAL 616.00 - 0 TROK_SVE_OK_I NORMAL 1,280.00 RANGE SCAN 1 TROK_TAB_RESEAU_I NORMAL 728.00 - 0 TROK_VOIE_I NORMAL 624.00 - 0 ****************************** ************************* ********** ----------- ---------- sum 6,152.00 21 NC_TRANSACTION_PRIHOD_T TRPRH_DATUM_I NORMAL 872.00 RANGE SCAN 15 TRPRH_ISNC_I NORMAL 830.00 - 0 TRPRH_IZLAZ_I NORMAL 777.00 FAST FULL SCAN 1 TRPRH_ULAZ_I NORMAL 785.00 - 0 ****************************** ************************* ********** ----------- ---------- sum 3,264.00 16 NC_TRANSACTION_T NC_TR_INSERT_DATE_I NORMAL 936.00 RANGE SCAN 1 RESEAU_GARE_I NORMAL 1,024.00 RANGE SCAN 331 TAB_RESEAU_I NORMAL 1,024.00 RANGE SCAN 7 TR_BROJ_CSC_I NORMAL 360.00 RANGE SCAN 7 TR_DATUM_IZLAZA_I NORMAL 1,024.00 RANGE SCAN 1,312 FULL SCAN 2 TR_DATUM_ULAZA_I NORMAL 815.00 RANGE SCAN 50 TR_GRUPA_PROIZVODA_I NORMAL 608.00 - 0 TR_ID_OBS_MP_I NORMAL 832.00 - 0 TR_ID_OBS_SEQUENCE_I NORMAL 640.00 - 0 TR_ID_PAIEMENT_I NORMAL 768.00 RANGE SCAN 447 TR_MATRICULE_I NORMAL 1,152.00 - 0 TR_MODE_REGLEMENT_I NORMAL 896.00 RANGE SCAN 5 TR_MOP_ISSUER_CODE_I NORMAL 168.00 - 0 TR_RTRIM_I NORMAL 592.00 - 0 TR_TDATE_I NORMAL 1,216.00 RANGE SCAN 73 TR_TICKET_I NORMAL 1,024.00 RANGE SCAN 436 ****************************** ************************* ********** ----------- ---------- sum 14,103.00 2,671 NC_TRANSACTION_ULAZ_T TRU_DATUM_IZLAZA_I NORMAL 952.00 RANGE SCAN 731 FAST FULL SCAN 1 TRU_DATUM_ULAZA NORMAL 600.00 - 0 TRU_GRUPA_PROIZVODA_I NORMAL 992.00 - 0 TRU_ID_OBS_SEQUENCE_I NORMAL 688.00 - 0 TRU_IZLAZ_DATE_I NORMAL 616.00 RANGE SCAN 20 FULL SCAN 7 TRU_MATRICULE_I NORMAL 1,152.00 - 0 TRU_RTRIM_I NORMAL 992.00 RANGE SCAN 15 TRU_TDATE_I NORMAL 1,153.00 - 0 ****************************** ************************* ********** ----------- ---------- sum 8,713.00 774 NC_TRANSACTION_ULAZ_TT TRUT_I NORMAL 774.00 RANGE SCAN 1 FULL SCAN 1 TRUT_GRUPA_PROIZVODA_I NORMAL 888.00 FULL SCAN 1 TRUT_MATRICULE_I NORMAL 848.00 - 0 TRUT_RESEAU_GARE_I NORMAL 784.00 FULL SCAN 1 TRUT_RTRIM_I NORMAL 888.00 FULL SCAN 6 ****************************** ************************* ********** ----------- ---------- sum 4,956.00 10 P_OPIZNOP IOO_OPI_FK_I NORMAL 104.00 RANGE SCAN 84 IOO_PK NORMAL 112.00 RANGE SCAN 42 FAST FULL SCAN 1 ****************************** ************************* ********** ----------- ---------- sum 328.00 127 Z_PLACENO PLC_MT_I NORMAL 166.56 SAMPLE FAST FULL SCAN 1 PLC_OBP_I NORMAL 136.00 RANGE SCAN 13 ****************************** ************************* ********** ----------- ---------- sum 302.56 14 154 rows selected. Showed only indexes in TAB schema whose size > 100 MB in period: 26.10.2010-30.11.2010What is interesting here (keep in mind that monitoring period is 46 days!):
- NC_CASH_UP_DETAIL_T table was probably a play yard for some testing and player has forget it about that. After public mail announcement, drop all three indexes ASAP. But if you have space leave it-looks like this table is forgotten in your storage!
- All indexes with "Executions" value=0 has never been used in monitored period. Reason may be poor quality (against underlying data) or change of CBO plans. These indexes are highly on the list for purge-especially if they are big or they are under table with big use (on other indexes i.e. NSO_PROD_I)!
- Big indexes with small number of executions, especially those with just one execution, are really candidate to be purged and created/dropped for each execution
- Indexes where FULL SCAN is using mode are candidates for rebuild or checking it's quality*
- Tables which have many indexes with small index usage are candidates for relation model redesign
- Indexes with huge number of executions should be observed a lot. Rebuild it when usage in next period decrease for more then 15%-25%.
Index quality*
Mentioned index quality was firstly published on Jonathan Lewis blog and Dan has shown it's version:
/********************************************************************** * File: Index_Info.sql * Type: SQL*Plus script * Author: Dan Hotka * Date: 04-16-2009 * * Description: * SQL*Plus script to display Index Statistics in relation to clustering factor * Script originated from Jonathan Lewis bug I have heavily modified it * Modifications: * *********************************************************************/ set linesize 300 spool index_info.txt SELECT i.table_name, i.index_name, t.num_rows, t.blocks, i.clustering_factor, case when nvl(i.clustering_factor,0) = 0 then 'No Stats' when nvl(t.num_rows,0) = 0 then 'No Stats' when (round(i.clustering_factor / t.num_rows * 100)) < 6 then 'Excellent ' when (round(i.clustering_factor / t.num_rows * 100)) between 7 and 11 then 'Good' when (round(i.clustering_factor / t.num_rows * 100)) between 12 and 21 then 'Fair' else 'Poor' end Index_Quality, i.avg_data_blocks_per_key, i.avg_leaf_blocks_per_key, to_char(o.created,'MM/DD/YYYY HH24:MI:SSSSS') Created from user_indexes i, user_objects o, user_tables t where i.index_name = o.object_name and i.table_name = t.table_name order by 1; spool offIdea of this script is to measure index quality. If index has bad ratio of index_clustering_factor/table_rows it's usage may be worse then full table scan! There are mainly two reasons why index has bad quality (without deeper coverage any of these terms):
- Index is fragmented-solution is to rebuild index
- Underlying table data are in that order that usage of index is really doubt-able. Solution is to drop it (make invisible in 11g!) and try to execute query to see performance. In a case of huge performance downgrade think about creation of index before query and drop after is the best solution for rarely run queries.
SQL> @index_q NSO_I Data represented for INDEX like "NSO_I" Table Table Index Data Blks Leaf Blks Clust Index Table Rows Blocks Index Size MB per Key per Key Factor Quality --------------------- ----------- ---------- ------ --------- --------- --------- ------------ ----------- NC_SUBSCRIPTION_SALDO 22,461,175 415103 NSO_I 560.00 9 1 21,749,007 1-PoorThis is classical representative of index with enormous clustering factor who may be a candidate for rebuild (very doubtful on first look) and if that doesn't help then recreate/drop should eb an option if not and pure drop!
SQL> @index_q TRA_I Data represented for INDEX like "TRA_I" Table Table Index Data Blks Leaf Blks Clust Index Table Rows Blocks Index Size MB per Key per Key Factor Quality --------------------- ----------- ---------- ------ --------- --------- --------- ------------ ----------- NC_TRANSACTION_ARZ_T 5,815,708 137742 TRA_I 184.00 12 1 222,104 5-ExcellentThis is representative of high quality index who is unused (used only twice). For such an index I'll recommend deeper analyze for involved SQLs. But this is not a candidate for purge in any mean.
SQL> @index_q TROK_I Data represented for INDEX like "TROK_I" Table Table Index Data Blks Leaf Blks Clust Index Table Rows Blocks Index Size MB per Key per Key Factor Quality --------------------- ---------- ---------- ------- --------- --------- --------- ------------ ----------- NC_TRANSACTION_OK_T 24,492,333 851796 TROK_I 536.00 1 1 21,977,784 1-PoorThis index has never been used-highly recommended for purge!
SQL> @index_q TRUT_I Data represented for INDEX like "TRUT_I" Table Table Index Data Blks Leaf Blks Clust Index Table Rows Blocks Index Size MB per Key per Key Factor Quality --------------------- ---------- ---------- ------- --------- --------- --------- ------------ ----------- NC_TRANSACTION_ULAZ_TT 840,179 27437 TRUT_I 774.00 1 1 731,609 1-PoorThis index is pretty big (774 MB), with low index quality, and use only twice. Recommendation rebuild and, if that doesn't help, purge.
The end
Manipulating indexes and keep them in good shape are in many cases very important. Fortunately, perfect Oracle algorithms for index balance, on normally driven OLTP database (without many deletion or purging), make us less worry in this DBA area. However checking their usage is not bad idea from time to time. Hope that this article helps someone in this task.Cheers!
count between 0 and 5
ReplyDeleteshould be
count between 1 and 5
;-)
Correct!
ReplyDeleteHowever, it is not a bug it is a "feature"!
;-)
PPPP_VALUE_I shows as having had 1 full scan. However it may also have had 10,000 range scans in the same period, which wouldn't appear as the count is beyond the bounds.
ReplyDeleteIs that correct, or is there something that I've mis-understood ?
Gary,
ReplyDeleteNo it is not correct.
Mine solution shows all occurrences of index/index_operation combination (look at group by clause).
To prove that, please look at "IR_DJK_KONTROLIRAN_OD_FK_I". It has two entries (FULL SCAN and RANGE SCAN). If (for that index) there was "SAMPLE FAST FULL SCAN" or "FAST FULL SCAN" usage, it will be in the list as well.
So one occurrence in the list means really just one use in monitored period.
Damir
Hi!
ReplyDeleteRegardless I didn't realize comment from Gary in the firs place, now I found out that something was wrong in script.
After reviewing, now should all be OK-and I have expand possibility with additional parameter and ability to see unused indexes as well.
Because I have modified script, output has been changed (as well as some data because "something was purged") I have posted new output so previous comment from Gary may not be found.
@Gary,
THX, for pointing me a problem and sorry for misunderstanding in first place.
;-)
interesting article
ReplyDeleteits the script also working in local partitioned indexes ???
Script work with any kind of index (look in dba_indexes entries)
DeleteRg,
Damir
This comment has been removed by the author.
ReplyDeleteHi,
ReplyDeleteYour posts are awesome. Could you share the script name "index_q" please?
Regards
Boris
Hi!
ReplyDeleteTHX for an info ... Here it is:
set linesize 145
set pagesize 1000
clear computes
clear breaks
break on TABLE_NAME on NUM_ROWS on BLOCKS
column owner format a14 heading 'Index owner'
column table_name format a25 heading 'Table'
column index_name format a25 heading 'Index'
column num_rows format 999G999G990 heading 'Table|Rows'
column MB format 9G990 heading 'Index|Size MB'
column blocks heading 'Table|Blocks'
column num_blocks format 9G990 heading 'Data|Blocks'
column avg_data_blocks_per_key format 999G990 heading 'Data Blks|per Key'
column avg_leaf_blocks_per_key format 999G990 heading 'Leaf Blks|per Key'
column clustering_factor format 999G999G990 heading 'Clust|Factor'
column Index_Quality format A13 heading 'Index|Quality'
SPOOL index_quality
PROMPT Data represented for INDEX like "%&&1%"
SELECT
i.table_name,
t.num_rows,
t.blocks,
i.index_name,
o.bytes/1048576 MB,
i.avg_data_blocks_per_key,
i.avg_leaf_blocks_per_key,
i.clustering_factor,
CASE when nvl(i.clustering_factor,0) = 0 then '0-No Stats'
when nvl(t.num_rows,0) = 0 then '0-No Stats'
when (round(i.clustering_factor / t.num_rows) * 100) < 6 then '5-Excellent'
when (round(i.clustering_factor / t.num_rows) * 100) between 7 and 11 then '4-Very Good'
when (round(i.clustering_factor / t.num_rows) * 100) between 12 and 15 then '2-Good'
when (round(i.clustering_factor / t.num_rows) * 100) between 16 and 25 then '2-Fair'
else '1-Poor'
END Index_Quality
from dba_indexes i,
dba_segments o,
dba_tables t
where
i.index_name like upper ('%&&1%') and
i.owner=t.owner and
i.table_name = t.table_name and
i.owner=o.owner and
i.index_name = o.segment_name
order by TABLE_NAME, NUM_ROWS, BLOCKS, Index_Quality DESC;
SPOOL OFF;
PROMPT Usage: "index_quality [SCHEMA]"
PROMPT RUN index_usage.sql (for index use) and index_frag.sql (for rebuild indexes)
Hi!
ReplyDeleteNow I found out that was a small bug in original script and in last post as well.
when (round(i.clustering_factor / t.num_rows) * 100) < 6 then '5-Excellent'
when (round(i.clustering_factor / t.num_rows) * 100) between 7 and 11 then '4-Very Good'
when (round(i.clustering_factor / t.num_rows) * 100) between 12 and 15 then '2-Good'
when (round(i.clustering_factor / t.num_rows) * 100) between 16 and 25 then '2-Fair'
change to
when (round(i.clustering_factor / t.num_rows * 100)) < 6 then '5-Excellent'
when (round(i.clustering_factor / t.num_rows * 100)) between 7 and 11 then '4-Very Good'
when (round(i.clustering_factor / t.num_rows * 100)) between 12 and 15 then '2-Good'
when (round(i.clustering_factor / t.num_rows * 100)) between 16 and 25 then '2-Fair'
:-)
Someone has posted a script on a blog which will work without the need of AWR (license). Is this alternate script any good? http://iziadmin.wordpress.com/2011/10/11/oracle-index-usage/
ReplyDelete@anonymous
DeleteYour solution is interesting with next limitations:
1) v$ vies are part of the data dictionary. So they are cleared when instance is restarted. 2) v$ cannot keep entries if instance is up for a longer time. So numbers of executions are wrong.
3) Your solution does not says what is with indexes not used.
Rg,
Damir
Let me show with numbers. Query which period is in dictionary:
Deleteselect min (timestamp) firstdate, max(timestamp) lastdate
from gv$sql_plan c
where c.operation = 'INDEX'
FIRSTDATE LASTDATE
------------------- -------------------
19.03.2012 09:29:25 20.03.2012 16:19:40
As you see, 7 hours is in case.
:-)
Hi Damir
ReplyDeleteExcellent post and very useful script. Could you please share index_frag.sql script?
Thank you
Haris
Hi!
DeleteThis is not part of solution presented here but mine (untested scripts) which I do not share ...
Hope you do understand.
Rg
Damir
Hi Damir,
ReplyDeleteThank you for giving us more help.
In your post you mentioned below:
All indexes with "Executions" value=0 has never been used in monitored period. Reason may be poor quality (against underlying data) or change of CBO plans.
As you know, all execution plan are from AWR and default top sql is 30,It means some sql would not be gathered.
Does the execution value 0 still mean the index never used when I pass the second parameter 5MB or less?
Hope you to give me a bright.
Thanks,
Robinson
Correct.
DeleteThis sql's limited number is why I use this for 100 MB and bigger indexes, whose sql are always in first 30 in snapshot.
:-)
With 5 MB indexes you should be really careful if your database has bigger (and heavier) queries
rg
Damir
Hi Damir,
ReplyDeleteThank you for your reply.
Still have some question. I found some total size for index columns were not correct.e.g.
P_OPIZNOP IOO_OPI_FK_I NORMAL 104.00 RANGE SCAN 84
IOO_PK NORMAL 112.00 RANGE SCAN 42
FAST FULL SCAN 1
****************************** ************************* ********** ----------- ----------
sum 328.00 127
For the table P_OPIZNOP,the real result of index columns should be 216,but current value is 328.
Could you explain that?
Robinson
As you see:
Delete104.00 RANGE SCAN 84
IOO_PK NORMAL 112.00 RANGE SCAN 42
FAST FULL SCAN 1
****************************** ************************* ********** ----------- ----------
sum 328.00
Sum is not correct because it works only when each table/index combination is shown once ... this is how sql report feature do. Haven't find a way to fix this. If you remove "Index operation" coulmn it will make correct sum ...
Really nice post....Thanks....
ReplyDeleteThat was enjoyable to read, thanks for posting it.
ReplyDeletemy webpage Bing (http://www.bing.com/)
why can't you use check querying v$sql?
ReplyDeletev$sql has a small data retention. After the cursor has been aged out of the shared pool is gone.
DeleteWhy setting retention for 46 days is better? I know it's longer time than 30 days, but why it's better?:)
ReplyDeleteScript is awesome! Thank you :)
You are covering full month and has some spare days ... you know that on 1st each month some scripts runs ... so somitemes they failed and prolonged a few days...and this is exactly why you have "spare 15 days"
DeleteThanks :)
DeleteExcellent Script. Thanks
ReplyDeleteExcellent!! Very useful.
ReplyDeletewhat did you mean by "Underlying table data are in that order that usage of index is really doubt-able" ?
ReplyDeleteif you have situation that using an index would be worse that having full table scan than underlying table data are in that order that are not appropriate for using that index.
DeleteHello Damir,
ReplyDeleteYour solution is great and we are using it on version 11! It works like a charm! However, on version 12.1.0.2 it is not working. It returns only zero executions on all indexes. Have you tested it on version 12?
Regards,
Hristiyan
It should work.
DeleteAre you conencted to container database or pluggable database?
I assume this is your problem.
Other think that might be a problem is that AWR didn't took any of snapshots of index use...this is mostly for very fast queries ...
Good, but index quality does not prove whether this index will be needed in the future. For example, I found indexes on PK that were not used but have index quality = Excellent. In any case, for me, they should stay in the base and shouldn't be touched, rebuilt, etc.
ReplyDelete