Oracle_高级功能(2) 索引

1.oracle优化器
优化目标分为4种:
choose (选择性)
rule (基于规则)
first rows(第一行)
all rows(所有行)
Description:描述sql的执行计划
Object owner:对象模式
Object name:对象名
Cost:花费(的时间)
Cardinality:基数,约等于行数
Bytes:空间(访问的存储空间)

2.table的访问方式
2.1全表扫描(table access full)
全表扫描就是顺序地访问表中每条记录。
oracle采用一次读入多个数据块(database block)的方式优化全表扫描。
举例:
select * from emp;

2.2通过rowid访问表(table access by user/index rowid)
可以采用基于rowid的访问方式情况,提高访问表的效率。
rowid包含了表中记录的物理位置信息。
oracle采用索引(index)实现了数据和存放数据的物理位置(rowid)之间的联系。
通常索引提供了快速访问rowid的方法,因此那些基于索引列的查询就可以得到性能上的提高。
举例:
select rowid,emp.* from emp where rowid='AAAR3sAAEAAAACXAAA';
select * from emp where empno=7788;

--清除数据库内存缓冲区
alter system flush buffer_cache;

索引(index)
1.索引定义
索引是一种与表相关联的可选结构,有时可以提高数据访问速度。
通过在表中的一个或多个列上创建索引,
通常情况下能够(快速地)从随机分布的表行中检索一小部分行。
索引是减少磁盘 i/o 的许多手段之一。
如果一个表没有索引,数据库必须执行全表扫描来查找值。
测试:
select * from all_objects;
create table tab_all_objects as select * from all_objects;
select * from tab_all_objects;
35s-->26s
select * from tab_all_objects where object_name='STUDENT';
0.531s-->0.015s
create index idx_object_name on tab_all_objects (object_name);
练习:
select owner,view_name,text_length,editioning_view,read_only from all_views;
create table tab_all_views as select owner,view_name,text_length,editioning_view,read_only from all_views;
select * from tab_all_views;
0.515s-->0.498s
select * from tab_all_views where view_name='VIEW_EMP';
0.499s-->0.062s
create index idx_view_name on tab_all_views (view_name);

select * from emp,dept where emp.deptno=dept.deptno;
0.109s-->0.031s
create index idx_emp_deptno on emp (deptno);
通常,在下列情况下可以考虑在某列上创建索引:
某个列经常被作为查询条件,并且查询结果只返回了表中的一小部分行。5% >20%
列或列集上存在引用完整性约束(是外键列)。

二、语法
2.1 创建索引
create [unique | bitmap] index 索引名 on 表名 { ([<expr>] <col> [asc | desc] [, …])
举例:
create index idx_emp_deptno on emp (deptno); 普通索引
create unique index idx_emp_ename on emp (ename); 唯一索引
create index idx_emp_job_sal on emp (job,sal); 复合索引
create index idx_emp_sal on emp (sal*12); 函数索引

select * from emp where deptno=20;
select * from emp where ename='FORD';
select * from emp where job='MANAGER' and sal>=2500;
select * from emp where sal*12>=20000;

select * from tab_all_objects where object_id=74975;
create index idx_object_id on tab_all_objects (object_id);

--select * from tab_all_objects where object_type='SYNONYM';
--create index idx_object_type on tab_all_objects (object_type);

2.2 修改索引
alter index <ind> {enable | disable};
alter index <ind> rename to <new>;
举例:
alter index IDX_EMP_SAL disable;
alter index idx_emp_deptno disable;
alter index IDX_EMP_SAL rename to IDX_EMP_SAL1;
alter index IDX_EMP_SAL1 rename to IDX_EMP_SAL;

2.3 删除索引
drop index <ind>;
举例:
--drop index IDX_EMP_SAL;

2.4 分析索引
analyze index <ind> validate structure online|offline;
举例:
analyze index IDX_EMP_SAL validate structure online;

2.5 重建索引
alter index <ind> rebuild
举例:
alter index IDX_EMP_SAL rebuild;

--授权
connect sys/123 as sysdba;
grant create any index,drop any index,alter any index to scott;


三、索引特征
索引是一种模式对象,它在逻辑上和物理上都与其相关联表对象的数据保持独立。
因此,可以删除或创建索引而不会实际影响相关的表。
如果删除一个索引,应用程序将仍然可以工作。不过,访问之前通过索引访问的数据可能会变慢。
索引的存在与否,不需要改变任何sql语句的写法。
索引是到单一行数据的快速访问路径。它只影响执行的速度。
对于一个已被索引的给定的数据值,索引直接指向包含该值的行的位置。
在表上存在过多的索引,会降低dml性能,因为数据库还必须更新索引。
主键和唯一键会自动生成索引,但需要手动在外键上创建索引。
create index ind_emp_deptno on emp_test (deptno);
select * from emp,dept where emp.deptno=dept.deptno;

四、复合索引
复合索引,也称为连接索引,是在某个表中的多个列上的索引。
复合索引中的列应该以在检索数据的查询中最有意义的顺序出现,但在表中不必是相邻的。
若 where 子句引用了复合索引中的所有列或前导列,
复合索引可以加快 select 语句的数据检索速度。
所以,在定义中所使用的列顺序很重要。一般地,最常被访问的列放在前面。

五、唯一索引和非唯一索引
索引可以是唯一的或非唯一的。
唯一索引保证在表的键列或键列集上没有具有重复的值的行。
在一个唯一索引中,对每个数据值都存在一个 rowid。叶块中的数据仅根据键排序。
非唯一索引允许在索引的列或列集中有重复的值。
对于非唯一索引,rowid 被包含在键中且已排序,
因此非唯一索引按索引键和 rowid (升序) 进行排序。

六、索引类型
索引类型分为:b-树索引、位图索引、基于函数的索引

6.1 b-树索引
这是索引的标准类型。对于主键和高选择性索引非常适合。
平衡树,简称b-树,是最常见的数据库索引类型。
一个 b-树索引是被划分为多个范围的已排序的值列表。
通过将键与一行或行范围关联起来 ,b-树可以对多种类型的查询提供优秀的检索性能,
包括精确匹配和范围搜索等。

二分查找算法是在有序数组中用到的较为频繁的一种算法。
在未接触二分查找算法时,
最通用的一种做法是,对数组进行遍历,跟每个元素进行比较,其时间为O(n).
但二分查找算法则更优,因为其查找时间为O(lgn)。
比如数组{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},查找元素6,
用二分查找的算法执行的话,其顺序为:
1.找中位数,用中位数和6比较,如果中位数比6大,则在中位数左侧数组中查找;如果中位数比6小,则在中位数右侧数组中查找;
2.循环步骤1,直到等于要查找的值,并返回;或者找不到要查找的值,则返回空。

select index_type,count(1) from dba_indexes where 1=1 group by index_type order by count(1) desc;
1 NORMAL 3681
2 LOB 901 BLOB、CLOB列产生的,非单独索引(view PLAN_TABLE$;)
3 IOT - TOP 158 索引组织表的主键索引(view SQLLOG$;)
4 FUNCTION-BASED NORMAL 40 基于函数的索引(view DBFS$_MOUNTS;)
5 BITMAP 16 位图索引
6 CLUSTER 10
7 FUNCTION-BASED DOMAIN 4
8 DOMAIN 1

create bitmap index SH.SALES_PROD_BIX on SH.SALES (PROD_ID);


6.2 位图索引
在位图索引中,索引条目使用位图来指向多个行。
create index idx_emp_deptno on emp (deptno);
drop index idx_emp_deptno;
create bitmap index idx_emp_deptno on emp (deptno);

6.3 基于函数的索引
这种类型的索引包括经过一个函数(如upper函数)转换过的列,或包括在表达式中的列。
create index idx_emp_ename_lower on emp (lower(ename));

七、索引扫描
在索引扫描中,数据库使用语句指定的索引列,通过遍历索引来检索行。
数据库扫描索引,将使用n个i/o就能找到其要查找的值,其中 n 即是b-树索引的高度。
这是数据库索引背后的基本原理。
如果 sql 语句仅访问被索引的列,那么数据库只需直接从索引中读取值,而不用读取表。
如果该语句同时还需要访问除索引列之外的列,那么数据库会使用 rowid 来查找表中的行。
通常,为检索表数据,数据库以交替方式先读取索引块,然后读取相应的表块。

7.1.完全索引扫描 INDEX FULL SCAN
select emp.* from emp,dept where emp.deptno=dept.deptno;
在完全索引扫描中,数据库顺序读取整个索引。
如果在 sql 语句中的谓词 (where 子句) 引用了一个索引列,
或者在某些情况下未不指定任何谓词,此时可能使用完全索引扫描。
完全扫描可以消除排序,因为数据本身就是基于索引键排过序的。


7.2快速完全索引扫描 INDEX FAST FULL SCAN
select view_name from tab_all_views;
--all rows
快速完全索引扫描是一种完全索引扫描,数据库并不按特定的顺序读取索引块。
数据库仅访问索引本身中的数据,而无需访问表。
当索引包含了查询所需的所有列,且索引键中至少一列具有 not null 约束时,快速完全索引扫描可以替代全表扫描。

7.3 索引范围扫描 INDEX RANGE SCAN
create index idx_emp_deptno on emp (deptno);
select * from emp where deptno=10;
索引范围扫描是对索引的有序扫描,具有以下特点:
在条件中指定了一个或多个索引前导列。
条件指定一个或多个表达式和逻辑 (布尔) 运算符的组合,并返回一个值( true、 false,或unknown)。
一个索引键可能对应0个、1个或更多个值。
通常,数据库使用索引范围扫描来访问选择性的数据。
选择性是查询所选择的数据占总行数的百分比, 0 意味着没有任何行,1 表示所有行。
选择性与一个(或多个)查询谓词相关,比如where last_name like 'a%'。
值越接近 0的谓词越具有选择性,相反,越接近1的谓词则越不具有选择性。

7.4 唯一索引扫描 INDEX UNIQUE SCAN
select * from emp where empno=7788;

相对于索引范围扫描,唯一索引扫描必须是 有0个 或 1个 rowid 与索引键相关联。
当一个谓词使用相等运算符引用了唯一索引键的所有列时,数据库将执行唯一扫描。
只要找到了第一个记录,唯一索引扫描就停止处理,因为不可能有第二个记录满足条件。

7.5 索引跳跃扫描 INDEX SKIP SCAN
如果在复合索引前导键列中有少量不同值,而在非前导键列中有大量不同值,此时使用跳跃扫描是有益的。
--建表
create table stu
(
sex varchar2(1) not null,
sid number(8)
);
--建复合索引
create index idx_stu_sexandsid on stu(sex,sid);
--插数据
declare
begin
for i in 1..1000 loop
insert into stu values('M',i);
end loop;
for i in 1001..2000 loop
insert into stu values('F',i);
end loop;
end;
--分析表
analyze table stu compute statistics;
--分析SQL执行计划
select * from stu where sid=100;
索引跳跃扫描使用复合索引的逻辑子索引。
数据库“跳跃地”通过单个索引,好像它在多个单独的索引中搜索一样。
当在查询谓词中未指定组合索引的前导列时,数据库可能选择索引跳跃扫描。

索引是根据指定的数据库表列建立起来的顺序。
它提供了快速访问数据的途径,并且可监督表的数据,使其索引所指向的列中的数据不重复。
分为单列索引和组合索引。
主键、唯一键系统自动创建索引,外键系统不会自动创建索引。
外键不创建索引时将导致父子表连接查询时出现子表的全表扫描。

伪列rowid
1.定义:
oracle数据库的表中的每一行数据都有一个唯一的标识符,或者称为rowid,
在oracle内部通常就是使用它来访问数据的。
rowid需要 10个字节的存储空间,并用18个字符来显示。
该值表明了该行在oracle数据库中的物理具体位置。
可以在一个查询中使用rowid来表明查询结果中包含该值。
select rowid,emp.* from emp where rowid='AAAR3sAAEAAAACXAAA';
73196

2.存储
保存rowid需要10个字节或者是80个位二进制位。
这80个二进制位分别是:
1. 数据对象编号,表明此行所属的数据库对象的编号,每个数据对象在数据库建立的时候
都被唯一分配一个编号,并且此编号唯一。数据对象编号占用大约32位。
2. 对应文件编号,表明该行所在文件的编号,表空间的每一个文件标号都是唯一的。
文件编号所占用的位置是10位。
3. 块编号,表明改行所在文件的块的位置块编号需要22位。
4. 行编号,表明该行在行目录中的具体位置行编号需要16位。
这样加起来就是80位。

3.显示
Oracle的物理扩展ROWID有18位,每位采用64位编码,分别用A~Z、a~z、0~9、+、/共64个字符表示。
A表示0,B表示1,……Z表示25,a表示26,……z表示51,0表示52,……,9表示61,+表示62,/表示63。
64位编码表示的ROWID有18位,其中:
数据对象编号占6位;
文件编号占3位;
块编号占6位;
行编号占3位。
select log(2,64),log(2,64)*18 from dual;

4.举例:
select rowid,empno,ename from emp;
将会得到结果:
AAAR3sAAEAAAACXAAA
说明:
AAAR3s是数据库对象编号,AAE是文件标号,AAAACX是块编号,最后三位AAA(SMITH)是行编号。

5.验证
5.1 验证行编号
SMITH --> AAA
ALLEN --> AAB
不同人名 行号是递增的。
5.2 验证数文件标号
select FILE_ID as fid,FILE_NAME from dba_data_files where TABLESPACE_NAME='USERS' ;
FID FILE_NAME
---------- ---------------------------------------------
4 D:APPWANGXUWEIORADATAORCLUSERS01.DBF
FILE_ID=4,就是ROWID中AAE。
5.3 验证数据库对象编号
select * from dba_objects where object_name='EMP';
objectid
73196
73196 = AAAR3s ???
select ascii('R')-ascii('A') from dual;
R=17=17×64×64

3=26+26+3=55
select ascii('s')-ascii('a')+26 from dual;
s=44
AAAR3s=17×64×64+55×64+44
select 17*64*64+55*64+44 from dual;
73196

5.4 验证块编号
select * from dba_extents where segment_name='EMP';
extent_id =0
file_id =4
block_id =144
bytes =65536
blocks =8
144 = AAAACX ???
select ascii('X')-ascii('A') from dual;
23
AAAACX=2×64+23=
select 2*64+23 from dual;
151
AAAACX=151<>144 ???

6.dbms_rowid包
通过dbms_rowid包,可以直接得到具体的rowid包含的信息:
select dbms_rowid.rowid_object(rowid) object_id,
dbms_rowid.rowid_relative_fno(rowid) file_id,
dbms_rowid.rowid_block_number(rowid) block_id,
dbms_rowid.rowid_row_number(rowid) row_number,
rowid,emp.*
from emp;

通过dbms_rowid包,还可以查询到表或记录所在的文件
select file_id,file_name from dba_data_files
where file_id in (select distinct dbms_rowid.rowid_relative_fno(rowid) from scott.emp);
file_id file_name
4 D:APPORADATAORCLUSERS01.DBF

原文地址:https://www.cnblogs.com/BradMiller/p/9279753.html