Oracle数据库索引概述
索引介绍
索引是一种与表或表簇相关联的可选结构,有时可以提高数据访问速度。
通过在表中的一个或多个列上创建索引,在某些情况下使您能够(快速地)从随机分布的表行中检索一小部分行。
索引是减少磁盘 I/O 的许多手段之一。
如果一个堆组织表没有索引,数据库必须执行全表扫描来查找值。例如,如果没有索引,为查询 hr.departments 表中的位置 2700,数据库需要搜索每个表块中的每一行,以找到该值。当数据量增加时,这种方法性能会急速下降。
索引结构,DML语句操作的时候,会自动维护索引结构。基于此,需要注意,索引结构在DML操作时会消耗额外的资源。再进一步思考,表上应该尽量只在需要的列上建立索引,表上索引的数量保持适量。
通常,在下列情况下可以考虑在某列上创建索引:
要索引的列经常被查询,并只返回表中的行的总数的一小部分。
在索引的列或列集上存在引用完整性约束。索引可以避免当你更新父表主键、合并父表、从父表删除行时可能引起的全表锁定。
要在表上设置唯一键约束,并且想手动指定索引和所有索引选项。
索引特征
索引是一种模式对象,它在逻辑上和物理上都与其相关联的对象中的数据保持独立。因此,可以删除或创建索引而不会实际影响相关的表。
如果您删除一个索引,应用程序将仍然可以工作。不过,访问之前索引过数据可能变慢。
索引的存在与否,不需要改变任何 SQL 语句的写法。
索引是到单一行数据的快速访问路径。它只影响执行的速度。对于一个已被索引的给定的数据值,索引直接指向包含该值的行的位置。
在创建索引后,数据库会自动维护并使用它们。数据库还会自动反映对相关表数据的更改,如添加、 更新、和删除行等,用户不需要对所有相关索引做任何操作。即使在插入行时,被索引数据的检索性能仍然几乎不变。但是,在表上存在过多的索引,会降低 DML 性能,因为数据库还必须更新索引。
索引属性
可用性
索引可能是可用的 (默认值) 或不可用的。不可用索引在 DML 操作中不会被维护,并会被优化程序忽略。不可用索引可以提高大容量加载的性能。不用删除索引并稍后重新创建它,可以使索引不可用,最后再重新生成它。不可用索引或索引分区不会占用空间。当把一个可用的索引置为不可用时,数据库将删除其索引段。
可见性
索引可能是可见的 (默认值) 或不可见的。不可见索引在 DML 操作中会被维护,但在默认情况下优化程序不会使用它。使索引不可见是使其不可用或删除它的一种替代方法。不可见索引有时特别有用,比如在删除索引前测试移除后果,或临时用一下索引而不会影响整个应用程序。
-- 创建索引时指定不可见属性
create index index_name on table_name(column_list) invisible;
-- 后期调整可见性
alter index index_name invisible;
alter index index_name visible;
复合索引
复合索引,也称为联合索引,是在某个表中的多个列上的索引。
复合索引中的列应该以在检索数据的查询中最有意义的顺序出现,这些列在表中可以是不相邻的。
若 WHERE 子句引用了复合索引中的所有列或前导列,复合索引可以加快SELECT 语句的数据检索速度。所以,在定义中所使用的列顺序很重要。一般地,最常被访问的列放在前面。
CREATE INDEX employees_ix ON employees (last_name, job_id, salary);
如果查询会访问所有的三个列,或仅 last_name 列,或仅 last_name 和job_id 列,则会使用此索引。在本例中,不访问 last_name 列的查询,将不会使用索引。
唯一索引和非唯一索引
索引可以是唯一的或非唯一的。唯一索引保证在表的键列或键列集上没有具有重复的值的行。例如,没有任何两名雇员,可以有相同的雇员 id。因此,在一个唯一索引中,对每个数据值都存在一个 rowid。叶块中的数据仅根据键排序。
非唯一索引允许在索引的列或列集中有重复的值。例如,雇员表的first_name 列中可能包含多个 Mike 值。对于非唯一索引,rowid 被包含在键中且已排序,因此非唯一索引按索引键和 rowid (升序) 进行排序。
空值与索引
除了位图索引或簇键列值为空的情况之外,Oracle 数据库不会索引所有键列都为空的表行。
索引存储
Oracle 数据库将索引数据存储在一个索引段 中。在一个数据块中对索引数据可用的空间是,数据块大小减去块开销、 条目开销、 rowid 、和每个被索引的值所需的一个字节。
索引段的表空间,是所有者的默认表空间,或在 CREATE INDEX 语句中明确指定的表空间。
为了便于管理,可以在一个独立于表所在表空间的单独表空间中存储索引。例如,可以选择不备份仅包含索引的表空间,因为索引表空间可以重建,这样可以减少备份所需要的时间和存储空间。
索引类型
B-树索引
这是索引的标准类型。他们对于主键和高选择性索引非常适合。
在复合索引中使用时,B-树索引可以按多个索引列以排序方式检索数据。
B-树索引具有下列子类型:
索引组织表
索引组织表不同于堆组织表,因为数据本身就是索引,表中所有行数据就存储在索引结构中,mysql数据库中表默认就是以该方式存储数据。
反键索引
在这种类型的索引中,索引键中的字节被反转了,例如, 103被存储为 301。反转字节可以把对索引记录的插入分散到的很多数据块。
降序索引
这种类型的索引将存储在一个特定的列或多列中的数据按降序排序。
B-树簇索引
这种类型的索引用于索引表簇键。它的键指向包含簇键相关行所在块,而不是指向行。
位图索引和位图联接索引
在位图索引中,索引条目使用位图来指向多个行。相比之下, B-树索引条目指向单个行。位图联接索引是在两个或更多表的联接上的位图索引。。
基于函数的索引
这种类型的索引包括经过一个函数(如 UPPER 函数)转换过的列,或包括在表达式中的列。
B-树索引或位图索引都可以是基于函数的。
应用程序域索引
这种类型的索引是由用户为一个特定的应用程序域中的数据创建的。其物理索引不需要使用传统的索引结构,可以存储为 Oracle 数据库表,或外部文件。
B-树索引
平衡树,简称 B-树,是最常见的数据库索引类型。B树索引将排序的值列表划分为多个范围。通过将键与单行或一个范围内的行关联起来,B树能够在各类查询(包括精确匹配查询和范围搜索)中提供出色的检索性能。
如图:

分支块和页块
B-树索引有两种类型的块: 用于查找的分支块和用于存储值的叶块。B-树索引的上层分支块包含指向下层索引块的索引数据。
B-树索引之所以是平衡的,是因为所有叶块都自动处于相同的深度。因此,在索引中从任意位置检索任意记录需要的时间基本上是相同的。
索引的高度是从根块到叶块所需的块的数量。分支级别等于其高度减 1。
分支块用于存储在两个键之间作出分支决定所需的最小键前缀。这种技术使数据库在每个分支块上能够尽可能存放更多的数据。分支块包含一个指针,该指针指向包含该键的子块。键和指针的数量受限于块大小。
叶块包含每个被索引的数据值,和一个相应的用来定位实际行的 rowid。每个条目按 (键,rowid)排序。
在一个叶块内,键和 rowid 链接到其左右同级条目叶块,使得这些叶块本身也双向链接在一起。
索引扫描
在索引扫描中,数据库使用语句指定的索引列,通过遍历索引来检索行。
数据库扫描索引,将使用 n 个 I/O 就能找到其要查找的值,其中 n 即是 B-树索引的高度。这是数据库索引背后的基本原理。
如果 SQL 语句仅访问被索引的列,那么数据库只需直接从索引中读取值,而不用读取表。
如果该语句同时还需要访问除索引列之外的列,那么数据库会使用 rowids 来查找表中的行。
通常,为检索表数据,数据库以交替方式先读取索引块,然后读取相应的表块。
完全索引扫描
在完全索引扫描中,数据库顺序读取整个索引。如果在 SQL 语句中的谓词(WHERE 子句) 引用了一个索引列,或者在某些情况下未不指定任何谓词,此时可能使用完全索引扫描。
完全扫描可以消除排序,因为数据本身就是基于索引键排过序的。
假设应用程序运行以下查询:
SELECT department_id, last_name, salary
FROM employees
WHERE salary > 5000
ORDER BY department_id, last_name;
此外假定 department_id last_name,和 salary 是一个复合索引键。
Oracle 数据库会执行完全索引扫描,按顺序读取 (按部门 ID 和姓氏顺序) 并基于薪金属性进行筛选。通过这种方式,数据库只需扫描一个小于雇员表的数据集,而不用扫描那些未包含在查询中的列,并避免了对该数据进行排序。
例如,完全扫描可能这样读取索引条目,如下所示:
50,Atkinson,2800,rowid
60,Austin,4800,rowid
70,Baer,10000,rowid
80,Abel,11000,rowid
80,Ande,6400,rowid
110,Austin,7200,rowid
快速完全索引扫描
快速完全索引扫描是一种完全索引扫描,数据库并不按特定的顺序读取 索引块。
数据库仅访问索引本身中的数据,而无需访问表。
当索引包含了查询所需的所有列,且索引键中至少一列具有 NOT NULL 约束时,快速完全索引扫描可以替代全表扫描。
例如,应用程序发出以下查询,不包含 ORDER BY 子句:
SELECT last_name, salary
FROM employees;
如果姓氏和工资(last,salary)是一个复合索引键,那么快速完全索引扫描只需读取索引条目,就可以获取所需的信息:
Baida,2900,rowid
Zlotkey,10500,rowid
Austin,7200,rowid
Baer,10000,rowid
Atkinson,2800,rowid
Austin,4800,rowid
索引范围扫描
索引范围扫描是对索引的有序扫描,具有以下特点:
在条件中指定了一个或多个索引前导列。
条件指定一个或多个表达式和逻辑 (布尔) 运算符的组合,并返回一个值( TRUE、 FALSE,或 UNKNOWN)。
一个索引键可能对应 0 个、1 个或更多个值。
通常,数据库使用索引范围扫描来访问选择性的数据。
选择性是查询所选择的数据占总行数的百分比, 0 意味着没有任何行,1 表示所有行。选择性与一个(或多个)查询谓词相关,比如 WHERE last_name LIKE 'A%'。值越接近 0 的谓词越具有选择性,相反,越接近 1 的谓词则越不具有选择性。
例如,用户查询姓氏以 A 开头的雇员。假定 last_name 列已被索引,其索引条目如下所示:
Abel,rowid
Ande,rowid
Atkinson,rowid
Austin,rowid
Austin,rowid
Baer,rowid
. .
数据库可以使用范围扫描,因为在谓词中指定了 last_name 列,而且每个索引键可能对应多个 rowids。例如有两个雇员名叫 Austin,所以有两个rowids 都与索引键 Austin 相关联。
索引范围扫描可以在两边都有边界,比如部门 ID 在 10 至 40 之间的查询,或只在一边有界,比如部门 id 在 40 以上的查询。
为扫描索引,数据库将在叶块之间前后移动 。例如,对于 ID 在 10 到 40 之间的扫描,将先定位到包含最低键值 (大于或等于 10)的第一个索引叶块。然后顺着各个被链接的叶块水平推进,直到它找到一个大于 40 的值为止。
唯一索引扫描
相对于索引范围扫描,唯一索引扫描必须是 有 0 个 或 1 个 rowid 与索引键相关联。
当一个谓词使用相等运算符引用了唯一索引键的所有列时,数据库将执行唯一扫描。
只要找到了第一个记录,唯一索引扫描就停止处理,因为不可能有第二个记录满足条件。
假定用户运行如下查询:
SELECT *
FROM employees
WHERE employee_id = 5;
假定 employee_id 列是主键,并具有如下的索引条目:
1,rowid
2,rowid
4,rowid
5,rowid
6,rowid
在这种情况下,数据库可以使用唯一索引扫描来定位 rowid,以找到 ID 为 5 的雇员
索引跳跃扫描
索引跳跃扫描使用复合索引的逻辑子索引,逻辑子索引的数目决定于前导列中的非重复值的数目。
数据库"跳跃地"通过单个索引,好像它在多个单独的索引中搜索一样。
如果在复合索引前导键列中有少量不同值,而在非前导键列中有大量不同值,此时使用跳跃扫描是有益的。
当在查询谓词中未指定组合索引的前导列时,数据库可能选择索引跳跃扫描。
例如,假定您要在 sh.customers 表中查找一个客户,运行如下查询:
SELECT * FROM sh.customers WHERE cust_email = 'Abbey@company.com';
customers 表中有一列 cust_gender,其值为 M 或 F。假定在列cust_gender 和 cust_email 上存在一个复合索引
复合索引条目
F,Wolf@company.com,rowid
F,Wolsey@company.com,rowid
F,Wood@company.com,rowid
F,Woodman@company.com,rowid
F,Yang@company.com,rowid
F,Zimmerman@company.com,rowid
M,Abbassi@company.com,rowid
M,Abbey@company.com,rowid
虽然未在 WHERE 子句中指定 cust_gender,数据库可以使用跳跃索引扫描。
前导列中有两个可能值。数据库在逻辑上将该索引拆分为一个具有 F 键的子索引和另一个具有 M 键的子索引。
当搜索电子邮件为 Abbey@company.com 的客户的记录时, 数据库首先搜索 F值的子索引,然后搜索 M 值的子索引。
从概念上讲,数据库这样处理查询,如下所示:
SELECT * FROM sh.customers WHERE cust_gender = 'F' AND cust_email = 'Abbey@company.com'
UNION ALL
SELECT * FROM sh.customers WHERE cust_gender = 'M' AND cust_email = 'Abbey@company.com';
索引聚簇因子
索引聚簇因子用于测量相对于某个索引值的行顺序,被索引值的行存储得越有序,则聚簇因子越低(简单理解,表中的索引列数据如果是按顺序存放的,那么聚簇因子越低)。
作为一种粗略测量通过索引读取整个表所需的 I/O 数,聚簇因子非常有用:
如果聚簇因子较高,则在大型索引范围扫描过程中,数据库将执行相对较高数目的 I/O。索引条目指向随机表块,因此数据库可能必须一遍又一遍地来回重读索引所指向的同一数据块。
如果聚簇因子较低,则在大型索引范围扫描过程中数据库将执行相对较低数目的 I/O。在一个范围内的索引键倾向于指向相同的数据块,因此该数据库不必来回重读相同的数据块。
聚簇因子与索引扫描关系密切,因为它可以显示:
数据库是否会在大范围扫描中使用索引
相对于索引键的表组织程度
如果行必须按索引键顺序排列,是否应考虑使用索引组织表、 分区、或表簇
简单点理解
如果聚簇因子较低,索引范围扫描,需要扫描的表块少,I/O总量就少,性能相对更高。
如果聚簇因子较低,索引范围扫描,需要扫描的表块更多,I/O总量就更多,性能相对降低。
示例:
通过 ALL_INDEXES 查看这两个索引的聚簇因子。
select index_name,CLUSTERING_FACTOR
FROM ALL_INDEXES
WHERE INDEX_NAME IN
('EMP_NAME_IX','EMP_EMP_ID_PK');
INDEX_NAME CLUSTERING_FACTOR
-------------------- -----------------
EMP_EMP_ID_PK 19
EMP_NAME_IX 2
EMP_NAME_IX 的聚簇因子较低,这意味着在一个单一叶块中的相邻索引条目倾向于指向同一个数据块中的行。
EMP_EMP_ID_PK 的聚簇因子较高,这意味着在相同的叶块中的相邻索引条目不太可能指向同一个数据块中的行。
反向键索引
反向键索引是一种 B-树索引,它在物理上反转每个索引键的字节 ,但保持列顺序不变。
例如,如果索引键是 20,,并且在一个标准的 B-树索引中此键被存为十六进制的两个字节 C1 15, 那么反向键索引会将其存为 15,C1。
反向键解决了在 B-树索引右侧的的叶块争用问题。在 Oracle 真正应用集群(Oracle RAC) 数据库中的多个实例重复不断地修改同一数据块时,这个问题尤为严重。例如,在订单表中,订单的主键是连续的。在集群中的一个实例添加订单 20,而另一个实例添加订单 21,每个实例都将它的键写入索引右侧的相同叶块。
在一个反向键索引中,对字节顺序反转,会将插入分散到索引中的所有叶块。例如键 20 和 21,本来在一个标准键索引中会相邻,现在存储在相隔很远的独立的块中。这样,顺序插入产生的 I/O 被更均匀地分布了。
因为存储数据时,并没有按照键列排序,因此在某些情况下,反向键格式丧失了执行索引范围扫描查询的能力。例如,如果用户发出一个订单 id 大于20 的查询,但数据库不能从包含此 ID 的块开始扫描并沿着叶块水平推进。
升序和降序索引
对于升序索引,数据库按升序排列的顺序存储数据。
创建的索引默认为升序索引。
默认情况下,字符数据按每个字节中包含的二进制值排序, 数值数据按从小到大排序,日期数据从早到晚排序。
升序索引示例
CREATE INDEX emp_deptid_ix ON hr.employees(department_id);
Oracle 数据库对 hr.employees 表按 department_id 列进行排序。从 0 开始,按 department_id 列及相应的 rowid 值的升序顺序加载索引。使用此索引,数据库搜索已排序的 department_id 值,并使用相关联的 rowids 来定位包含所请求的 department_id 值的行。
通过在 CREATE INDEX 语句中指定 DESC 关键字 ,可以创建一个降序索引。
在这种情况下,索引在指定的一列或多列上按降序顺序存储数据。
当要求查询按一些列升序而另一些列降序排序时,降序索引非常有用。
例如,假定在 last_name 列和 department_id 列上创建一个复合索引,如下所示:
CREATE INDEX emp_name_dpt_ix ON hr.employees(last_name ASC, department_id DESC);
一个对 hr.employees 用户查询,要求按姓氏以升序顺序 (A 到 Z) 而部门id 以降序 (从高到低)排序,则数据库可以使用此索引检索数据并避免额外的排序步骤。
键压缩
Oracle 数据库可以使用键压缩来压缩 B-树索引或索引组织表中的主键列值的部分。
键压缩可以大大减少索引所使用的空间。
一般地,索引键包含两个片断,一个分组片断和一个唯一片断。
键压缩将索引键分成两部分,即作为分组片断的前缀条目,和作为唯一或几乎唯一片断的后缀条目。
数据库通过在一个索引块中的多个后缀条目之间共享前缀条目来实现压缩。
如果某个键未被定义为具有唯一片断,那么数据库会通过将一个 rowid 附加到分组片断来提供唯一片断。
默认情况下,一个唯一索引的前缀由除最后一列之外的所有键列组成,而非唯一索引的前缀包含所有键列。
例如,假设您在 oe.orders 表上创建一个复合索引,如下所示:
CREATE INDEX orders_mod_stat_ix ON orders( order_mode, order_status );
在 order_mode 和 order_status 列中有许多重复值出现。一个索引块中的条目可能如示例中所示。
示例 订单表中的索引条目
online,0,AAAPvCAAFAAAAFaAAa
online,0,AAAPvCAAFAAAAFaAAg
online,0,AAAPvCAAFAAAAFaAAl
online,2,AAAPvCAAFAAAAFaAAm
online,3,AAAPvCAAFAAAAFaAAq
online,3,AAAPvCAAFAAAAFaAAt
键前缀将包括 order_mode 和 order_status 的串联值。
如果此索引按默认键压缩创建,那么重复键前缀(如 online,0 和online,2)将会被压缩。
从概念上讲,数据库按如下示例中所示实现压缩:
online,0 online,0
AAAPvCAAFAAAAFaAAa AAAPvCAAFAAAAFaAAa
AAAPvCAAFAAAAFaAAg AAAPvCAAFAAAAFaAAg
AAAPvCAAFAAAAFaAAl AAAPvCAAFAAAAFaAAl
online,2 online,2
AAAPvCAAFAAAAFaAAm AAAPvCAAFAAAAFaAAm
online,3 online,3
AAAPvCAAFAAAAFaAAq AAAPvCAAFAAAAFaAAq
AAAPvCAAFAAAAFaAAt AAAPvCAAFAAAAFaAAt
后缀条目形成索引行的压缩版本。每个后缀条目引用一个与其存储在相同索引块中的前缀条目。
或者,创建一个压缩索引时,也可以指定前缀长度。
例如,如果指定了前缀长度为 1 ,那么前缀将会是 order_mode,而后缀将会是 order_status,rowid。
对于示例中的数值,索引会提取重复出现的 online 作为前缀,如下所示:
online
0,AAAPvCAAFAAAAFaAAa 0,AAAPvCAAFAAAAFaAAa
0,AAAPvCAAFAAAAFaAAg 0,AAAPvCAAFAAAAFaAAg
0,AAAPvCAAFAAAAFaAAl 0,AAAPvCAAFAAAAFaAAl
2,AAAPvCAAFAAAAFaAAm 2,AAAPvCAAFAAAAFaAAm
3,AAAPvCAAFAAAAFaAAq 3,AAAPvCAAFAAAAFaAAq
3,AAAPvCAAFAAAAFaAAt 3,AAAPvCAAFAAAAFaAAt
对索引中的每个叶块,一个特定前缀最多被存储一次。
只有在 B-树索引的叶块中的键会被压缩。
在分支块中的键后缀可以被截断,但不会被压缩。
位图索引
在位图索引中,数据库为每个索引键存储一个位图。在传统的 B-树索引中,一个索引条目指向单个行。
在位图索引中,每个索引键存储指向多个行的指针。
位图索引主要用于数据仓库,或在以特定方式引用很多列的查询环境中。
可能需要一个位图索引的情况包括:
索引列的基数较低,也就是说,不同值的数目相比表的总行数很小。
被索引的表是只读的,或 DML 语句不会对其进行重大修改。
举一个数据仓库的例子, sh.customer 表的 cust_gender 列只有两个可能的值:M 和 F。假设会经常查询某一性别的客户数目。在这种情况下,可以考虑在 customer.cust_gender 列上创建位图索引。
位图中的每一位对应于一个可能的 rowid。如果设置了某位,那么与其相应的 rowid 行包含该键值。映射函数将位的位置转换为一个实际的 rowid,所以,虽然位图索引使用不同的内部表示形式,但它提供了与 B-树索引相同的功能。
如果更新了某个单行中的索引列,那么数据库将锁定整个索引键条目 (例如M 或 F) ,而不只是该位映射到的更新行。因为一个键指向多个行,DML通常会锁定索引数据的所有这些行。因此,位图索引并不适合许多 OLTP 应用程序。
单表的位图索引
单个表上创建的位图索引,即单表的位图索引
案例一:
示例,客户表查询,此表中的某些列是创建位图索引的候选对象。
SSELECT cust_id, cust_last_name, cust_marital_status, cust_gender
FROM sh.customers
WHERE ROWNUM < 8
ORDER BY cust_id;
CUST_ID CUST_LAST_ CUST_MAR C CUST_ID CUST_LAST_ CUST_MAR C
---------- ---------- -------- - ---------- ---------- -------- -
1 Kessel M 1 Kessel M
2 Koch F 2 Koch F
3 Emmerson M 3 Emmerson M
4 Hardy M 4 Hardy M
5 Gowen M 5 Gowen M
6 Charles single F 6 Charles single F
7 Ingram single F 7 Ingram single F
7 rows selected. cust_marital_status 和 cust_gender 列的基数较低,而 cust_id 和cust_last_name 列的基数较高。因此,在 cust_marital_status 和cust_gender 列上创建位图索引可能是恰当的。而在其它列上创建位图索引则可能没什么用处。相反,在这些列上创建 B-树唯一索引可能会提供最有效的表示形式和检索性能。
案例二
cust_gender 列的位图索引。它包括两个分别针对每个性别的单独位图。
值 行1 行2 行3 行4 行5 行6 行7
M 1 0 1 1 1 0 0
F 0 1 0 0 0 1 1
映射函数将位图中的每一位转换为客户表中的一个 rowid。每一位的值取决于表中的相应行中的值。例如, M 值的位图其第一位包含 1,因为客户表的第一行的性别是 M。对于位图 cust_gender ='M ', 与第 2、 6,7 行中相应的位为 0,因为这些行上包含的值不是 M。
注意:
与 B-树索引不同,位图索引可以包括完全由空值组成的键。对空值建立索引对于某些的 SQL 语句是有用的,比如使用 COUNT 聚合函数的查询。
调查客户的人口趋势的分析人员可能会问,"我们的女性客户中,有多少是单身或离婚的?" 这一问题对应于以下的 SQL 查询:
SELECT COUNT(*)
FROM customers
WHERE cust_gender = 'F' AND cust_marital_status IN ('single', 'divorced');
位图索引可以高效地处理此查询,通过计算得到的位图作为 1 值的数量。要确定满足条件的客户,数据库可以访问表使用得到的位图。
值 行1 行2 行3 行4 行5 行6 行7
M 1 0 1 1 1 0 0
F 0 1 0 0 0 1 1
single 0 0 0 0 0 1 1
divorced 0 0 0 0 0 0 0
single或divorced和 F 0 0 0 0 0 1 1
如果我们在cust_marital_status列上在创建一个位图索引,这个语句,我们可以使用位图索引合并技术。
位图索引可以有效地合并与 WHERE 子句中的多个条件相对应的索引。满足一些但不是全部条件的行,将在表本身被访问之前被过滤掉。这种技术往往大大提高了响应的时间。
位图联接索引
位图联接索引是建立在两个或更多表的联接之上的位图索引。对于表列中的每个值,索引存储被索引表中的相应行的 rowid。
通过在联接前预先执行限制条件,位图联接索引是减少数据量的有效方式。
案例:
举一个可能会用到位图联接索引的示例,假定用户经常查询某种特定职位类型的雇员数。一个典型的查询可能如下所示:
SELECT COUNT(*)
FROM employees, jobs
WHERE employees.job_id = jobs.job_id
AND jobs.job_title = 'Accountant';
上述查询通常使用 jobs.job_title 上的一个索引来检索职位表中的Accountant 行,然后通过(公共列)职位 ID,和员工表中employees.job_id 列上的索引来找到匹配的行。若要从索引本身检索数据,而不是从表中扫描,可以创建一个位图联接索引,如下所示:
CREATE BITMAP INDEX employees_bm_idx ON employees (jobs.job_title)
FROM employees, jobs
WHERE employees.job_id = jobs.job_id;
索引键是 jobs.job_title, 而被索引的表是employees。
从概念上讲,employees_bm_idx 是建立在所示的 SQL 查询中的 jobs.title 列上的索引。在索引中的 job_title 键指向雇员表中的行。对会计师数目的查询可以仅使用索引,而不用访问 employees表和 jobs 表,因为该索引本身已包含所请求的信息。
SELECT jobs.job_title AS "jobs.job_title",
employees.rowid AS "employees.rowid"
FROM employees, jobs
WHERE employees.job_id = jobs.job_id
ORDER BY job_title;
jobs.job_title employees. rowid
---------------------------- ------------------
Accountant AAAQNKAAFAAAABSAAL
Accountant AAAQNKAAFAAAABSAAN
Accountant AAAQNKAAFAAAABSAAM
Accountant AAAQNKAAFAAAABSAAJ
Accountant AAAQNKAAFAAAABSAAK
Accounting Manager AAAQNKAAFAAAABTAAH
Administration Assistant AAAQNKAAFAAAABTAAC
Administration Vice President AAAQNKAAFAAAABSAAC
Administration Vice President AAAQNKAAFAAAABSAAB

在数据仓库中,连接条件是在维度表主键列和事实数据表的外键列之间的一个等值连接 (使用相等运算符)。位图联接索引有时比物化视图的存储效率高得多,这是一种替代提前物化连接的方法。
位图存储结构
Oracle 数据库使用一个 **B-树索引结构来为每个索引键存储位图。**例如 ,如果 jobs.job_title 是一个位图索引的键列, 那么索引数据存储在一个 B-树中。单个位图存储在叶块中。
位图索引的数据存储在一个段中。Oracle 数据库将每个位图存储在一个或多个片断中。每个片断占一个单一数据块的一部分。
假定 jobs.job_title 列具有 Shipping Clerk、Stock Clerk、及其他几个唯一值。此位图索引的索引条目具有以下组件:
作为索引键的具体值
一个 rowids 范围的低值 rowid 和高值 rowid
在该范围内的特定 rowids 的位图
从概念上讲,该索引中的一个索引叶块,可能包含如下所示的条目:
Shipping Clerk,AAAPzRAAFAAAABSABQ,AAAPzRAAFAAAABSABZ,0010000100
Shipping Clerk,AAAPzRAAFAAAABSABa,AAAPzRAAFAAAABSABh,010010
Stock Clerk, AAAPzRAAFAAAABSAAa,AAAPzRAAFAAAABSAAc,1001001100
Stock Clerk, AAAPzRAAFAAAABSAAd,AAAPzRAAFAAAABSAAt,0101001001
Stock Clerk, AAAPzRAAFAAAABSAAu,AAAPzRAAFAAAABSABz,100001
同一职位可能出现在多个条目中**,这是因为其 rowid 范围不同**。
假定某个会话更新了一名雇员的职务 ID,从 Shipping Clerk 到Stock Clerk。在这种情况下,该会话需要对旧值(Shipping Clerk)和新值(Stock Clerk)所在索引键条目的独占访问权限。直到 UPDATE 提交之前,Oracle 数据库会锁定这两个条目所指向的所有行,而不是由 Accountant 或其他任何键所指向的行。
基于函数的索引
Oracle数据库可以基于函数、或涉及相关表的一个或多个列的表达式来创建索引。
基于函数的索引计算函数或涉及一个或多个列的表达式的值,并将其存储在索引中。
基于函数的索引可以是一个 B-树索引或位图索引。
用于生成索引的函数可以是算术表达式,或一个包含 SQL 函数、 用户定义PL/SQL 函数、 包函数,或 C 调用的表达式。 例如,函数可以将两个列中的值相加。
使用基于函数的索引
基于函数的索引对于在 WHERE 子句中包含函数计算的语句是有效的。仅当在查询中包含该函数时,数据库才使用基于函数的索引。当数据库处理INSERT 和 UPDATE 语句时,它仍然必须计算函数才能完成对语句的处理。
例如,假设您创建如下基于函数的索引:
CREATE INDEX emp_total_sal_idx ON employees (12*salary*commission_pct, salary, commission_pct);
当数据库处理如所示的查询时,它可以使用前面定义的索引。
SELECT employee_id, last_name, first_name, 12*salary*commission_pct AS "ANNUAL SAL"
FROM employees
WHERE (12 * salary * commission_pct) < 30000
ORDER BY "ANNUAL SAL" DESC;
EMPLOYEE_ID LAST_NAME FIRST_NAME ANNUAL SAL EMPLOYEE_ID LAST_NAME FIRST_NAME ANNUAL SAL
159 Smith Lindsey 28800
151 Bernstein David 28500
152 Hall Peter 27000 152
160 Doran Louise 27000
175 Hutton Alyssa 26400
149 Zlotkey Eleni 25200
169 Bloom Harrison 24000
基于 SQL 函数 UPPER(column_name) 或 LOWER(column_name)定义的索引,使对大小写无关的搜索变得非常方便。
例如,假设雇员表的 first_name 列包含混合大小写字符。您在 hr.employees 表上创建了如下基于函数的索引:
CREATE INDEX emp_fname_uppercase_idx ON employees ( UPPER(first_name) );
emp_fname_uppercase_idx索引使如下所示的查询变得非常方便:
SELECT *
FROM employees
WHERE UPPER(first_name) = 'AUDREY';
基于函数的索引对于只在一个表中的特定行上建立索引也是有用的。例如sh.customers 表中的 cust_valid 列有 I 或 A 两个值。如果只想对含 A 值的行建立索引,可以编写一个函数,让不含 A 值的其它任何行都返回一个空值。
可以这样创建索引,如下所示:
CREATE INDEX cust_valid_idx ON customers ( CASE cust_valid WHEN 'A' THEN 'A' END );
优化基于函数的索引
对于在 WHERE 子句中包含表达式的查询,优化程序可以在一个基于函数的索引上使用索引范围扫描。当谓词(WHERE 子句)的选择性很低时,范围扫描的访问路径显得特别有优势。
虚拟列可用于快速访问由表达式生成的数据,可以在虚拟列上创建索引。例如,可以为表达式12**salary*commission_pct 定义虚拟列 annual_sal,并在该annual_sal 列上创建一个基于函数的索引。
优化程序通过分析在 SQL 语句中的表达式来执行表达式匹配,然后比较的语句表达式目录树和基于函数的索引。这种比较不区分大小写,并忽略空格。
应用程序域索引
应用程序域索引是一个特定于应用程序的自定义索引。
很少使用这类索引,了解即可。
Oracle 数据库提供了可扩展的索引,以执行下列操作:
可以在自定义的、 复杂的数据类型(如文档、 空间数据、 图像、和视频剪辑等)之上建立索引.
使用专门的索引技术
可以将应用程序特定的索引管理例程封装为 indextype 的 模式对象,并在表中的列或对象类型属性上定义一个域索引。
可扩展的索引可以有效地处理特定于应用程序的操作。
由称为模块的应用软件,来控制域索引的结构和内容。数据库与应用程序进行交互,来建立、 维护、并搜索域索引。索引结构本身可以作为一个索引组织表存储在数据库中,或作为一个文件存储在数据库外部。