索引由 Oracle 服务器自动使用和维护,一旦一个索引被创建,它就不再需要用户直接管理。rowid是一个十六进制的串,表示包含块定义的行地址,行的位置在块中,并且有数据库文件标识符,访问任何指定行的最快的方法是引用它的 rowid.
索引里存储的内容: ROWID 、索引列(键值)。
创建索引可以大大提高系统的性能,总体来说,索引的优点如下所示:
- 大大加快数据的检索速度,这也是创建索引的最主要的原因。
- 索引可以加速表和表之间的连接。
- 索引在实现数据的参照完整性方面特别有意义,例如在外键列上创建索引可以有效的避免死锁的发生,
- 也可以防止当更新父表主键时,数据库对子表的全表锁定。
- 索引是减少磁盘 I/O 的许多有效手段之一。
- 当使用分组(GROUP BY)和排序(ORDER BY)子句进行数据检索时,可以显著减少查询中分组和排序的时间,大大加快数据的检索速度。
- 创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
- 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
索引的缺点如下所示:
- 索引必须创建在表上,不能创建在视图上。
- 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
- 建立索引需要占用物理空间,如果要建立聚簇索引,那么需要的空间会很大。
- 当对表中的数据进行增加、删除和修改的时候,系统必须要有额外的时间来同时对索引进行更新维护,以维持数据和索引的一致性,所以,索引降低了数据的维护速度
索引的使用原则:
- 在大表上建立索引才有意义。
- 在 WHERE 子句或是连接条件经常引用的列上建立索引。
- 索引的层次不要超过 4 层。
- 如果某属性常作为最大值和最小值等聚集函数的参数,那么考虑为该属性建立索引。
- 表的主键、外键必须有索引
- 创建了主键和唯一约束后会自动创建唯一索引。
- 经常与其它表进行连接的表,在连接字段上应该建立索引。
- 经常出现在 WHERE 子句中的字段,特别是大表的字段,应该建立索引。
- 要索引的列经常被查询,并只返回表中的行的总数的一小部分。
- 对于那些查询中很少涉及的列、重复值比较多的列尽量不要建立索引。
- 经常出现在关键字 ORDER BY、GROUP BY、DISTINCT 后面的字段,最好建立索引。
- 索引应该建在选择性高的字段上。
- 索引应该建在小字段上,对于大的文本字段甚至超长字段,不适合建索引。对于定义为 CLOB、TEXT、IMAGE 和 BIT 的数据类型的列不适合建立索引。
- 复合索引的建立需要进行仔细分析。正确选择复合索引中的前导列字段,一般是选择性较好的字段。
- 如果单字段查询很少甚至没有,那么可以建立复合索引;否则考虑单字段索引。
- 如果复合索引中包含的字段经常单独出现在 WHERE 子句中,那么分解为多个单字段索引。
- 如果复合索引所包含的字段超过 3 个,那么仔细考虑其必要性,考虑减少复合的字段。
- 如果既有单字段索引,又有这几个字段上的复合索引,那么一般可以删除复合索引。
- 频繁进行 DML 操作的表,不要建立太多的索引。
- 删除无用的索引,避免对执行计划造成负面影响
一个表中定义一个列为主键,或者定义一个唯一键约束时 Oracle 服务器自动创建该索引,索引的名字习惯上是约束的名字
另外,需要特别注意的是,索引不存储空值。
为什么不走索引(339页详解)
位图索引
- 适合于决策支持系统(DSS)或 OLAP 系统。位图索引主要用于数据仓库,或在以特定方式引用很多列的查询环境中。位图索引并不适合许多 OLTP 应用程序,若使用不当则容易产生死锁。
- 被索引的表是只读的,或 DML 语句不会对其进行频繁修改的表。
- 非常适合 OR 操作符的查询。
- 位图索引不直接存储 ROWID,而是存储字节位到 ROWID 的映射。
- 减少响应时间。
- 节省空间占用。
- 在同一列上建立位图索引后就不能再建立普通索引了,但是可以建立函数索引,位图索引可以和函数索引同时建立。
- 做 UPDATE 代价非常高。
- 基于规则的优化器不会考虑位图索引。
- 当执行 ALTER TABLE 语句并修改包含有位图索引的列时,会使位图索引失效。
- 位图索引不包含任何列数据,并且不能用于任何类型的完整性检查。
- 位图索引不能被声明为唯一索引。
- 位图索引的最大长度为 30。
- 位图索引适合创建在低基数列(即列值重复率很高)上。
函数索引
创建索引的函数里面不能使用 SUM、COUNT 等聚合函数
不能在 LOB 类型的列、NESTED TABLE 列上创建函数索引。
不能使用 SYSDATE、USER 等非确定性函数。
对于任何用户自定义函数必须显式的声明 DETERMINISTIC 关键字,否则会报错:“ora-30553: thefunction is not deterministic”
使用函数索引有几个先决条件:
(1)必须拥有 CREATE INDEX 和 QUERY REWRITE(本模式下)或 CREATE ANY INDEX 和 GLOBAL QUERY REWRITE(其它模式下)权限。
(2)必须使用基于成本的优化器,基于规则的优化器将被忽略。
(3)参数 QUERY_REWRITE_INTEGRITY 和 QUERY_REWRITE_ENABLED 可以保持默认值
索引组织表(IOT)
索引中存储的是行的实际数据,而不是 ROWID,它是基于主键访问数据的。在索引组织表中,索引就是数据,数据就是索引
辅助索引(Secondary Indexes)也叫二级索引,是一个建立在索引组织表上的索引。在某种意义上,它是一个索引的索引。辅助索引是一个独立的模式对象,并与索引组织表分开存储。索引表上的辅助索引能够非常有效地访问索引表,这种索引既不是使用主键列也不是使用主键的某个前缀。Oracle 构造这种索引使用的是逻辑的行标识符(Logical Rowids),这种 ROWIDS 是基于表的主键(而不是基于行的物理地址)。这种逻辑 ROWIDS 包括了一个物理猜想(Physical Guess),它也是一种物理标识符(物理指针),它唯一标识行的块地址,Oracle 能使用这些猜想直接对索引表的叶块进行探测,从而避免主键搜索。
当创建 IOT 时,必须要设定主键,否则报错。
分区索引
如果使用了位图索引,那么就必须是本地索引
本地分区索引可分为以下类别:
- 本地前缀索引(Local Prefixed Indexes)在这种情况下,分区键处于索引定义的前导部分。
- 本地非前缀索引(Local Nonprefixed Indexes)在这种情况下,分区键不是索引列列表的前导部分,甚至根本不必在该列表中。
CREATE INDEX IDX_PART_RANGE_ID ON T_PARTITION_RANGE(ID) LOCAL (
PARTITION I_RANGE_P1 TABLESPACE TS_DATA01,
PARTITION I_RANGE_P2 TABLESPACE TS_DATA02,
PARTITION I_RANGE_P3 TABLESPACE TS_DATA03,
PARTITION I_RANGE_PMAX TABLESPACE TS_DATA04
);
全局分区索引是一个 B-Tree 索引,全局索引(Global Index)既可以分区(全局分区索引),也可以不分区(普通索引),既可以建 RANGE分区,也可以建 HASH 分区,既可创建于分区表上,也可以创建于非分区表上,就是说,全局索引是完全独立的,因此,它也需要更多的维护操作
- 全局索引可以依附于分区表,也可以依附于非分区表。
- 全局分区索引的索引条目可能指向若干个分区,因此,对于全局分区索引,即使只截断一个分区中的数据,也需要 REBULID 若干个分区甚至是整个索引。
- 全局索引多应用于 OLTP 系统中。
- 全局分区索引只按 RANGE 或者 HASH 分区,HASH 分区是 Oracle 10g 以后才支持的。
- 在 Oracle 9i 以后对分区表做 MOVE 或者 TRUNCATE 的时候可以用 UPDATE GLOBAL INDEXES 语句来同步更新全局分区索引,用消耗一定资源来换取高度的可用性。
- 若在表中使用 A 列作分区,但在索引中用 B 列作本地索引,若 WHERE 条件中用 B 来查询,那么 Oracle会扫描所有的表和索引的分区,成本会比分区更高,此时可以考虑用 B 列做全局分区索引和用 A 列做本地索引。
- 在创建索引时,如果不显式指定 GLOBAL 或 LOCAL,那么默认是 GLOBAL。
- 在创建 GLOBAL 索引时,如果不显式指定分区子句,那么默认不分区。
- 含有子分区的分区索引有大小,但是在数据字典视图中的列 SEGMENT_CREATED 的值显示为 N/A,STATUS 的值也显示为 N/A。
Truncate 分区操作会导致全局索引失效; truncate 普通表对索引没有影响;
Truncate 分区操作不会释放全局索引中的空间,而truncate 普通表会释放索引所占空间;
rename 表名操作对索引没有影响,因为rename操作只是更改了数据字典,表中数据行的rowid并没有发生变化
全文索引
Oracle 全文索引使 Oracle 具备了强大的文本检索能力和智能化的文本管理能力。Oracle 将全文检索功能做为内置功能提供给用户,使得用户在创建数据库实例时自动安装全文检索。要使用 Oracle 全文索引,必须具有 CTXAPP 角色或者是 CTXSYS 用户。Oracle 全文索引为系统管理员提供 CTXSYS 用户,为应用程序开发人员提供 CTXAPP 角色。具有 CTXAPP 角色的用户可以使用全文索引
虚拟索引
不可见索引
ALTER INDEX INDEX_NAME INVISIBLE; --修改索引不可见
ALTER INDEX INDEX_NAME VISIBLE; --修改索引可见
不可见索引的特点主要有以下几点:
(1)当索引变更为不可见的时候,只是对 Oracle 的优化器不可见。
(2)不可见索引在 DML 操作的时候也会被维护。
(3)加 HNIT 对不可见索引无效。
(4)可以通过修改 SYSTEM 级别和 SESSION 级别参数来使用不可见索引。
反向键索引(Reverse Key Indexes)
只有对反向键索引列进行“=”操作时,其反向键索引才会使用。(在 WHERE 子句中使用“BETWEEN AND”语句或比较运算符“>”、“<”、“>=”、“<=”等),那么反向键索引将不会被使用
反向键索引应用场合:
1、在索引叶块成为热点块时使用
2、在 RAC 环境中使用
3、反向键索引通常建立在值是连续增长的列上,使数据均匀地分布在整个索引上。
创建索引
create index emp_last_name_idx on employees(last_name) online parallel 2; ---普通单列索引
CREATE INDEX IDX_CI_emp ON employees(OBJECT_ID,OBJECT_TYPE); ---复合索引(一般情况下,把最常被访问和选择性较高的列放在前面)
CREATE INDEX upper_last_name_idx ON employees(UPPER(last_name)); ---函数索引
CREATE BITMAP INDEX IDX_SEX_LHR ON T_USER(SEX); ---创建位图索引
CREATE TABLE IND_ORG_TAB_LHR(VENCODE NUMBER(4) PRIMARY KEY,VENNAME VARCHAR2(20)) ORGANIZATION INDEX; ---索引组织表
CREATE INDEX DOC_INDEX_LHR ON XT_DOCS_LHR(TEXT) INDEXTYPE IS CTXSYS.CONTEXT; ---创建全文索引
CREATE INDEX REV_INDEX_LHR ON XT_REVI_LHR(OBJECT_ID) REVERSE; ---创建反向键索引
DROP INDEX IDX_NAME_LHR FORCE; ---删除索引
查看索引信息
SELECT IC.INDEX_NAME,IC.COLUMN_NAME,IC.COLUMN_POSITION COL_POS,IX.UNIQUENESS FROM USER_INDEXES IX, USER_IND_COLUMNS IC WHERE IC.INDEX_NAME = IX.INDEX_NAME
AND IC.TABLE_NAME = 'EMPLOYEES'; ---查询某个表上的索引
SELECT D.INDEX_NAME,D.TABLE_NAME,D.COLUMN_NAME FROM DBA_IND_COLUMNS D WHERE D.INDEX_NAME='IDX_CI_20170628_LHR'; ---查询复合索引列
SELECT * FROM DBA_INDEXES D WHERE D.INDEX_TYPE LIKE 'FUNCTION-BASED%'; ---查询所有的函数索引
SELECT * FROM DBA_INDEXES D WHERE D.INDEX_TYPE='BITMAP'; ---查询所有的位图索引
SELECT * FROM DBA_INDEXES D WHERE D.JOIN_INDEX='YES'; ---查询所有的位图连接索引
SELECT * FROM DBA_TABLES D WHERE D.IOT_TYPE='IOT'; ---查询所有的IOT表
SELECT * FROM DBA_INDEXES D WHERE D.INDEX_TYPE LIKE '%/REV'; ---查询反向键索引
获取索引定义
select dbms_metadata.get_ddl('INDEX','INDEX_NAME','INDEX_OWNER') from dual;
索引选择性
SELECT INDEX_NAME,DISTINCT_KEYS/NUM_ROWS SELECTIVITY FROM DBA_INDEXES; 毋庸置疑,主键的选择性为 1。选择性越接近 1,那么该索引就越好。
查询索引大小
select owner,segment_name,(sum(bytes)/1024/1024)||'MB',tablespace_name from dba_segments where segment_name = 'IDX_T' group by owner,segment_name,tablespace_name;
---查询大索引
col owner for a10
col segment_name for a32
select * from (select owner,segment_name,bytes/1024/1024 SIZE_MB from dba_segments where owner in ('TEST') and segment_type like '%INDEX%' order by 3 desc) where rownum < 11;
Oracle 11gR2 中使用 EXPLAIN PLAN FOR CREATE INDEX 时,Oracle会提示评估的索引大小(ESTIMATED INDEX SIZE)
EXPLAIN PLAN FOR CREATE INDEX IDX_T ON SYS.TEST_INDEX_SIZE(OBJECT_ID); SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());

查询索引碎片比例
analyze index ID_IND validate structure; ---下面语句不要在其他会话查询 select name,del_lf_rows,lf_rows, round(del_lf_rows/decode(lf_rows,0,1,lf_rows)*100,0)||'%' frag_pct from index_stats where round(del_lf_rows/decode(lf_rows,0,1,lf_rows)*100,0)>30;
注意:x$kdxst是oracle的一个内存表,然后analyze index indexname validate structure这条语句会往x$kdxst写一些统计信息,当执行select * from x$kdxst的时候,oracle这里实际上是把你执行analyze index indexname validate structure时的那个indexname给当作绑定变量给传进来了,所以你只能看到你执行analyze index indexname validate structure的那个index的统计信息。也就是只有在执行过analyze index indexname validate structure后且在同一个session下才能从index_stats中看到数据
索引失效情况:
当某些操作导致数据行的 ROWID 改变,索引就会完全失效。可以分普通表和分区表来讨论哪些操作将导致索引失效。
(1)普通表索引失效的情形如下所示:
1、手动置索引无效:ALTER INDEX IND_OBJECT_ID UNUSABLE;。
2、如果对表进行 MOVE 操作(包含移动表空间和压缩操作)或在线重定义表后,那么该表上所有的索引状态会变为 UNUSABLE。MOVE 操作的 SQL 语句为:ALTER TABLE TT MOVE;。
3、SQL*Loader加载数据。在SQL*Loader加载过程中会维护索引,由于数据量比较大,在SQL*Loader加载过程中出现异常情况,也会导致Oracle来不及维护索引,导致索引处于失效状态,影响查询和加载。异常情况主要有:在加载过程中杀掉 SQL*Loader 进程、重启或表空间不足等。
(2)分区表索引失效的情形如下所示:
1、对分区表的某个含有数据的分区执行了 TRUNCATE、DROP 操作可以导致该分区表的全局索引失效,而分区索引依然有效,如果操作的分区没有数据,那么不会影响索引的状态。需要注意的是,对分区表的 ADD
操作对分区索引和全局索引没有影响。
2 执行 EXCHANGE 操作后,全局索引和分区索引都无条件地会被置为 UNUSABLE(无论分区是否含有数据)。但是,若包含 INCLUDING INDEXES 子句(缺省情况下为 EXCLUDING
INDEXES),则全局索引会失效,而分区索引依然有效。
3 如果执行 SPLIT 的目标分区含有数据,那么在执行 SPLIT 操作后,全局索引和分区索引都会被被置为UNUSABLE。如果执行 SPLIT 的目标分区没有数据,那么不会影响索引的状态。
4 对分区表执行 MOVE 操作后,全局索引和分区索引都会被置于无效状态。
5 手动置其无效:ALTER INDEX IND_OBJECT_ID UNUSABLE;。对于分区表而言,除了 ADD 操作之外,TRUNCATE、DROP、EXCHANGE 和 SPLIT 操作均会导致全局索引失
效,但是可以加上 UPDATE GLOBAL INDEXES 子句让全局索引不失效。重建分区索引的命令为:ALTER INDEX IDX_RANG_LHR REBUILD PARTITION P1;。
列出失效索引或索引分区
select owner, index_name, status From dba_indexes where status='UNUSABLE' and owner in ('FENG') order by 1,2; select index_owner,index_name,partition_name,subpartition_name from dba_ind_subpartitions where status='unusable' and index_owner in('FENG');
重建索引
alter index indexname rebuild online tablespace t1;
alter index indexname rebuild partition paritionname tablespace tablespacename; alter index indexname rebuild subpartition partitioname tablespace tablespacename;
alter index index_name coalesce
1、rebuild以index fast full scan(or table full scan) 方式读取原索引中的数据来构建一个新的索引,有排序的操作; rebuild online 执行表扫描获取数据,有排序的操作; 2、rebuild 会阻塞 dml 操作 ,rebuild online 不会阻塞 dml 操作; 3、rebuild online 时系统会产生一个 SYS_JOURNAL_xxx 的 IOT 类型的系统临时日志表,所有 rebuild online 时索引的变化都记录在这个表中,当新的索引创建完成后,把这个表的记录维护到新的索引中去,然后 drop 掉旧的索引 4、Rebuild操作会产生大量redo log;
5、使用带有coalesce参数时重建期间不需要额外空间,它只是在重建索引时将处于同一个索引分支内的叶块拼合起来,这最大限度的减少了与查询过程中相关的潜在的加锁问题,但是,coalesce选项不能用来将一个索引转移到其他表空间。
监控索引使用情况
监控索引一般有两种方式:
1)直接监控索引的使用情况
(1)设置所要监控的索引:ALTER INDEX IDX_T_XX MONITORING USAGE;
(2)查看该索引有没有被使用:SELECT * FROM V$OBJECT_USAGE;
(3)关闭监控:ALTER INDEX IDX_T_XX NOMONITORING USAGE;
2)schema级别索引监控
如果想在系统中监控所有的索引,可以通过下面脚本实现监控数据库所有的索引。注意我们要排除一些系统表的索引、以及LOB indexes。可以直接执行脚本来开启索引监控,当然监控索引时长非常重要,太短的话有可能导致查询出来的数据有问题,一般建议监控一周后即可,OLAP系统则需要适当延长监控的时间。
--1、开启索引监控 SELECT 'ALTER INDEX ' || owner || '.' || index_name || ' MONITORING USAGE;' enable_monitor, 'ALTER INDEX ' || owner || '.' || index_name || ' NOMONITORING USAGE;' disable_monitor FROM dba_indexes WHERE INDEX_TYPE != 'LOB' and owner IN (SELECT username FROM dba_users WHERE account_status = 'OPEN') AND owner NOT IN ('SYS', 'SYSTEM', 'PERFSTAT', 'MGMT_VIEW', 'MONITOR', 'SYSMAN', 'DBSNMP') AND owner not like '%SYS%'; --2、查询数据库中所有被监控索引的使用情况 SELECT U.NAME OWNER, IO.NAME INDEX_NAME, T.NAME TABLE_NAME, DECODE(BITAND(I.FLAGS, 65536), 0, 'NO', 'YES') MONITORING, DECODE(BITAND(OU.FLAGS, 1), 0, 'NO', 'YES') USED, OU.START_MONITORING START_MONITORING, OU.END_MONITORING END_MONITORING FROM SYS.USER$ U, SYS.OBJ$ IO, SYS.OBJ$ T, SYS.IND$ I, SYS.OBJECT_USAGE OU WHERE I.OBJ# = OU.OBJ# AND IO.OBJ# = OU.OBJ# AND T.OBJ# = I.BO# AND U.USER# = IO.OWNER#; --3、历史执行计划分析索引使用情况 WITH TMP1 AS (SELECT I.OWNER INDEX_OWNER, I.TABLE_OWNER, TABLE_NAME, INDEX_NAME, INDEX_TYPE, (SELECT NB.CREATED FROM DBA_OBJECTS NB WHERE NB.OWNER = I.OWNER AND NB.OBJECT_NAME = I.INDEX_NAME AND NB.SUBOBJECT_NAME IS NULL AND NB.OBJECT_TYPE = 'INDEX') CREATED, (SUM(S.BYTES) / 1024 / 1024) INDEX_MB, (SELECT COUNT(1) FROM DBA_IND_COLUMNS DIC WHERE DIC.INDEX_NAME = I.INDEX_NAME AND DIC.TABLE_NAME = I.TABLE_NAME AND DIC.INDEX_OWNER = I.OWNER) COUNT_INDEX_COLS FROM DBA_SEGMENTS S, DBA_INDEXES I WHERE I.INDEX_NAME = S.SEGMENT_NAME AND I.OWNER = S.OWNER AND S.OWNER NOT LIKE '%SYS%' GROUP BY I.OWNER, I.TABLE_OWNER, TABLE_NAME, INDEX_NAME, INDEX_TYPE HAVING SUM(S.BYTES) > 1024 * 1024), TMP2 AS (SELECT INDEX_OWNER, INDEX_NAME, PLAN_OPERATION, (SELECT MIN(TO_CHAR(NB.BEGIN_INTERVAL_TIME, 'YYYY-MM-DD HH24:MI:SS')) FROM DBA_HIST_SNAPSHOT NB WHERE NB.SNAP_ID = V.MIN_SNAP_ID) MIN_DATE, (SELECT MAX(TO_CHAR(NB.END_INTERVAL_TIME, 'YYYY-MM-DD HH24:MI:SS')) FROM DBA_HIST_SNAPSHOT NB WHERE NB.SNAP_ID = V.MAX_SNAP_ID) MAX_DATE, COUNTS FROM (SELECT D.OBJECT_OWNER INDEX_OWNER, D.OBJECT_NAME INDEX_NAME, D.OPERATION || ' ' || D.OPTIONS PLAN_OPERATION, MIN(H.SNAP_ID) MIN_SNAP_ID, MAX(H.SNAP_ID) MAX_SNAP_ID, COUNT(1) COUNTS FROM DBA_HIST_SQL_PLAN D, DBA_HIST_SQLSTAT H WHERE D.OPERATION LIKE '%INDEX%' AND D.SQL_ID = H.SQL_ID GROUP BY D.OBJECT_OWNER, D.OBJECT_NAME, D.OPERATION, D.OPTIONS) V) SELECT A.TABLE_OWNER, A.TABLE_NAME, A.INDEX_OWNER, A.INDEX_NAME, A.CREATED, A.INDEX_TYPE, A.INDEX_MB, A.COUNT_INDEX_COLS, B.PLAN_OPERATION, CASE WHEN MIN_DATE IS NULL THEN (SELECT MIN(TO_CHAR(NB.BEGIN_INTERVAL_TIME, 'YYYY-MM-DD HH24:MI:SS')) FROM DBA_HIST_SNAPSHOT NB) ELSE MIN_DATE END AS MIN_DATE, CASE WHEN MAX_DATE IS NULL THEN (SELECT MAX(TO_CHAR(NB.BEGIN_INTERVAL_TIME, 'YYYY-MM-DD HH24:MI:SS')) FROM DBA_HIST_SNAPSHOT NB) ELSE MAX_DATE END AS MAX_DATE, COUNTS FROM TMP1 A LEFT OUTER JOIN TMP2 B ON (A.INDEX_OWNER = B.INDEX_OWNER AND A.INDEX_NAME = B.INDEX_NAME);

查看某个表索引使用情况
某个用户的某个表的索引信息
--get the index column information by specified table set linesize 180 col cl_nam format a20 col table_name format a25 col cl_pos format 9 col idx_typ format a15 SELECT b.table_name, a.index_name, a.column_name cl_nam, a.column_position cl_pos, b.status, b.index_type idx_typ, a.descend dscd FROM dba_ind_columns a, dba_indexes b WHERE a.index_name = b.index_name AND owner = upper('&owner') AND a.table_name LIKE upper('%&table_name%') ORDER BY 2, 4;
--查看某个表索引使用情况 SELECT p.object_name, p.operation, p.options, COUNT(1) FROM v$sql_plan p, v$sql s WHERE p.object_owner <> 'SYS' AND p.OBJECT_NAME in (select index_name from dba_indexes where table_name = 'S_SHIP_UNIT_LINE') AND p.sql_id = s.sql_id GROUP BY p.object_name, p.operation, p.options ORDER BY 1, 2, 3; --查看历史索引使用情况 SELECT p.object_name, p.operation, p.options, COUNT(1) FROM dba_hist_sql_plan p, dba_hist_sqlstat s WHERE p.object_owner <> 'SYS' AND p.object_name in (select index_name from dba_indexes where table_name = 'S_SHIP_UNIT_LINE') AND p.sql_id = s.sql_id GROUP BY p.object_name, p.operation, p.options ORDER BY 1, 2, 3;
附录:
1、获得索引使用频率脚本(idx_usage_detail.sql)
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 set verify off col OWNER noprint col TABLE_NAME for a30 heading 'Table name' col INDEX_NAME for a30 heading 'Index name' col INDEX_TYPE for a15 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 ON
2、索引质量分析脚本
--1、script name: idx_quality.sql --Author : Leshami --index quality retrieval SET LINESIZE 145 SET PAGESIZE 1000 SET VERIFY OFF 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 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 AND t.owner = UPPER('&input_owner') AND t.table_name LIKE UPPER('%&input_tbname%') ORDER BY table_name, num_rows, blocks, index_quality DESC; --SPOOL OFF; ============================================================== --2、获取指定schema或表上的索引质量信息报告 SQL> @idx_quality.sql Enter value for input_owner: RFUSER Enter value for input_tbname: TAB_GOODSLABEL --省略具体的表名则会输出整个schema的索引质量报告