分享

第五章 模式对象

 月影斜 2010-05-08

Oracle 概念(Oracle 10.2) 第五章 模式对象

5、模式对象

本章描述了用户模式中包含的不同类型的数据库对象。

本章保安下列内容:

模式对象介绍

表概述

视图概述

实体化视图概述

维度概述

序列生成器概述

同义词概述

索引概述

索引组织表概述

应用领域索引概述

聚集概述

Hash聚集概述

模式对象介绍

模式(schema)是数据的逻辑结构或者说模式对象的汇总。一个模式对应一个数据库用户,并且名字和数据库用户名相同。每个用户都有一个单独的模式。模式对象可以通过SQL创建(DDL)和操作(DML),包含下列类型:

聚集

数据库链

数据库触发器

维度

外部过程库

索引和索引类型

Java类、Java资源和Java源代码

实体化视图和实体化视图日志

对象表、对象类型和对象视图

操作符

序列

存储函数、过程和包

同义词

表和索引组织表

视图

还有一些类型的对象可以保存在数据库中,也可以通过SQL创建和操作,但是不包含在模式中:

上下文(Contexts)

目录(Directory)

概要文件(Profile)

角色(Role)

表空间(Tablespaces)

用户(User)

模式对象是数据的逻辑存储结构。数据对象和磁盘上保存其信息的物理文件并不一一对应。Oracle在数据库的一个表空间上保存模式对象。每个对象的数据物理的保存在表空间的一个或者多个数据文件上。对某些对象如表、索引、聚集来说,你可以指定在表空间的数据文件上Oracle可以分配多大的磁盘空间来存储这个对象。

模式和表空间没有什么联系:一个表空间可以包换来自不同模式的对象,模式对象可以包含在不同的表空间上。

图5-1描述了对象、表空间、数据文件之间的关系。

图5-1 模式对象、表空间和数据文件

表概述

表是Oracle数据库内数据存储的基本单位。数据是按照行和列来存储。你用一个表名(比如employees)、一系列列来定义表。你为每列指定一个列名(比如employee_id,last_name和job_id等),一个数据类型(比如VARCHAR2,DATE,NUMBER)和一个宽度。宽度可以是数据类型比如日期预先定义好的。如果列是NUMBER类型,定义范围和精度就可以了。行是关联到一个记录的列信息的集合。

你可以指定表中每个列的规则。这些规则叫做完整性约束条件。NOT NULL就是一个完整性约束的例子。这个约束条件要求这列的每一行都包含一个值。

你还可以指定表列在保存在数据文件之前将数据加密。加密阻止了越过数据库访问控制来直接使用操作系统工具查看数据文件的现象。

在你创建表之后,使用SQL语句插入行数据。表的数据就可以使用SQL查询、删除和更新。

图5-2描述了一个实例表

 

图5-2 EMP表

表数据如何保存

当你创建表时,Oracle在一个表空间上自动分配一个数据段来保存表将来的数据。你有两种方式可以控制一个表数据段的分配和使用:

你可以通过设定数据段的存储参数来控制分配给数据段的空间大小

通过设置组成数据段的区段的数据块的参数PCTFREE和PCTUSED来控制块的空闲空间的使用。

对于聚集表来说,Oracle在针对聚集表创建的数据段上保存数据,而不是在表空间的上分配数据段保存数据。聚集表创建和修改时不能指定存储参数。聚集的存储参数设置控制了聚集中中所有表的存储。

表的数据段(或者聚集数据段,针对聚集表)可以创建在表拥有者的默认表空间上,或者在CREATE TABLE语句中指定。

行格式和大小

Oracle将一个包含至多256列的数据库行保存在一个或多个行片中。如果整个行能够插入一个数据块中,然后Oracle会保存这个行在一个行片中。虽然如此,如果所有的行数据不能插入一个数据块或者对一个现存行的更新造成行数据超出了它的数据块,然后Oracle会使用多个行片保存行。一个数据块通常一行只包含在一个行片中。当Oracle必须在多个行片中保存行时,它就是跨块链接的(行链接)。

当一个表拥有多余255个列,255之后的列的数据就会在同一块中链接(如果不超出块的话)。这个叫做块内部链接。行片链接是通过片的ROWID来链接起来的。通过块内部链接,用户可以从同一块中得到所有数据。如果行可以放在一个块中,用户不会感觉到I/O性能的影响,因为取得额外的行(255之后的行)不需要额外的I/O操作。

每个行片,无论链接还不是没有链接,都包含一个行首部(row header)和部分或全部行数据。单独的行可能跨越行片或者数据块。图5-3描述了行片的格式。

图5-3 行片的格式

行首部在数据的前面,包含如下信息:

行片

链接(只针对行片链接)

行片的列

聚集键(只针对聚集数据)

完全属于一个数据块的行至少拥有3个字节的行首部(row header)。在行首部信息之后,每个行包含列宽度和数据。列宽度需要在列数据之前保存,少于250个字节的列使用1个字节保存、多于250字节的列使用3个字节保存。列数据需要的空间依赖于列类型。如果列类型是变长的,那么保存这个值的空间需要随着数据更新增长或收缩。

为节省空间,一个null列只保存列长度(0)。Oracle不保存null列的数据。而且,对于尾部的null列来说,Oracle甚至都不保存列长度。

聚集行包含非聚集行同样的信息。除此之外,它们还包含指向他们所属的聚集键信息。

    行片的rowid

Rowid标识了每个行片的位置或地址。分配的rowid会一直保留,直到关联的行被删除或者使用Oracle工具导入导出为止。对聚集表来说,如果行的聚集键值改变,行会保留同样的rowid,但是会增加一个指向新值的rowid指针。

因为rowid对于行片的生命周期来说是一直保留的,例如SELECT,UPDATE和DELETE的SQL语句使用ROWID非常有用。

    列顺序

对一个给定的表来说,所有行的列顺序都是一致的。列的保存顺序通常和语句CREATE TABLE中显示的是一样的,但也不是必然的。例如,如果一个表拥有一个LONG数据类型的列,Oracle总是在最后保存这个列。而且如果修改一个表来增加一列,新列就变成最后保存的列。

通常来说,将经常为空的列保存在最后就可以节省空间。但是要注意的是,如果创建表包含一个LONG列,这个频繁为空的列就不能放在最后了。

表压缩

Oracle的表压缩特性通过减少数据块中重复值来压缩数据。数据块(磁盘页)中保存的压缩数据是自包含的。就是说,一个数据块中需要重新创建解压缩的所有数据信息对本数据块是可用的(一个块上的信息需要解压缩才能读取,但同样的信息在另一块上不需要)。一个块上的所有行和列的重复值都在块开始的地方保存一次,这个地方也叫块的符号表。这些(重复)值出现的地方都替代为指向符号表的链接。

除了开始的符号表之外,压缩数据库块看起来和通常数据库块非常类似。适用于通常数据库块的所有数据库特性和函数也适合压缩数据库块。

包括表和实体化视图的数据库对象都可以压缩。对于分区表来说,你可以选择压缩部分或者全部分区。压缩属性可以用在表空间、表或者分区表。如果在表空间级别声明压缩属性,那么其上创建的所有表默认都是压缩的。你可以修改一个表(或者表空间、分区表)的压缩属性,但只对新加入表的数据有效。因此,一个表或者分区可能同时包含压缩块和正常块。这样确保数据大小不能因为压缩而增加;在压缩会增加块使用时,块不会使用压缩。

使用表压缩

数据批量插入和批量装载时会导致压缩。包括如下操作:

SQL *Loader使用直接路径

CREATE TABLE和AS SELECT语句

并发INSERT(或使用提示APPEND串行INSERT)语句

数据库的已有数据可以通过ALTER TABLE和MOVE子句移动到压缩结构来压缩。这个操作会在表上加独占锁,从而会阻止任何更新和数据装载,直到压缩完成为止。如果客户不能接受这些,那么可以使用Oracle在线重定义工具(DBMS_REDEFINITION PL/SQL包)。

数据压缩在大部分数据类型上都可用,除了LOB的变体和源于LOB的数据类型,比如保存数组数据VARRAY类型保存存在CLOB中的XML数据类型。

压缩表或者压缩分区可以和其他Oracle表或分区一样的修改。例如,数据可以使用INSERT、UPDATE和DELETE语句更新。无论如何,不使用批量插入或批量装载技术修改的数据是没有压缩的。删除压缩数据和删除非压缩数据一样快。插入新数据也一样的快,因为常规的INSERT语句不使用压缩;只有在批量装载才会压缩。更新压缩数据在某些情况下会慢一些。由于这些原因,压缩更适合数据仓库应用程序,而不是OLTP应用程序。只读或者很少改变的部分数据(比如历史数据)应该使用压缩格式。

值NULL表示值不存在

Null代表行中的一列不存在值。Null代表丢失的、未知的或者不适用的数据。Null值不应该用来代表任何其他值,比如0。一个列如果定义了PRIMARY KEY和NOT NUL完整性约束就不允许null值,这时候任何行的列都必须指定值才能插入。

Null值在有值的列之间会保存。这时候,它需要保存列长度(0)。

行的尾null列不需要存储空间,因为后面的新行首部就说明了前面的行最后的列是null。例如,如果最后3列为null,这些列就不会保存任何信息。在设计表的列时,null概率较高的列应该定义在后面,这样可以节省空间。

null值和其他值的比较结果既不是真也不是假,是未知。在SQL中标识null,需要使用IS NULL。使用NVL函数可以将null值转换为非null值。

Null值是不能索引的,除非聚集键列值为null或者索引使用了位图索引。

列默认值

你可以指定表一个列的默认值,这样插入新行的时候可以忽略这个行值,或者可以使用DEFAULT关键字,这时候会自动提供默认值。默认值工作起来就如同INSERT语句指定了默认值一样。

默认文字或者表达式的数据类型必须符合或者可以转换为列的数据类型。

如果一列没有明确指定默认值,默认值就是null。

默认值插入和完整性约束

在包含默认值的行插入之后会启动完整性约束。例如,在图5-4中,一个插入到emp表的行不包括雇员的部门号的值。因为没有提供部门号的值,Oracle在deptno列中插入了默认值20.。插入默认值之后,Oracle检查定义在deptno列的外键完整性约束。

图5-4 列默认值

分区表

分区表允许你将你的数据分成更小的容易的管理的叫做分区的部分,甚至还可以包含子分区。索引可以按照类似的方式分区。每个分区可以单独管理,可以独立于其他分区进行操作,这样就针对可用性和性能方面提供了更好的调整结构。

嵌套表

你可以创建一个表,表中的列类型是另外一个表。就是说,表可以嵌套在另一个表中作为一个列的值。Oracle数据库服务器并不是把嵌套表保存在父表的行里,而是将一个存储表和嵌套表行关联。父表行包含一个唯一标识符来关联嵌套表实例。

临时表

除了持久表,Oracle还可以创建临时表来保存会话私有的数据,这些数据只在事务或会话期间保存。

CREATE GLOBAL TEMPORARY TABLE语句可以创建一个事务型或会话型临时表。对于事务型临时表,在事务持续期内数据存在。对于会话型临时表,会话持续期间数据存在。临时表中的数据是会话独立的。每个会话只能看到和修改自己的数据。DML锁不能加到临时表的数据上。LOCK语句对临时表的数据没有影响,因为每个会话拥有它们的私有数据。

会话型临时表的TRUNCATE语句删除当前临时表的数据。不能删除其他会话中使用同一个表的数据。

临时表上的DML语句产生的数据改变不会产生重做日志。但是会产生数据的undo日志和undo日志的重做日志。会话结束时临时表的数据会自动删除,包括用户登出或者会话意外终止(比如会话或实例失败)。

你可以使用CREATE INDEX语句来为临时表创建索引。临时表上的索引也是临时的,临时表上的索引数据也和临时表的数据一样在事务或者会话期间存在。

你还可以创建同时访问临时表和持久表的视图。你还可以创建临时表的触发器。

Oracle工具可以导入导出临时表的定义。但是,即使你使用ROWS子句也无法导出任何数据行。类似的,你可以复制历史表的定义,但是无法复制其数据。

段分配

临时表使用临时段。和持久表不同,临时表和他们的索引在他们创建时不会自动分配一个段。相反,段在第一个INSERT(或CREATE TABLE AS SELECT)语句执行时分配。这意味着在第一个INSERT之前执行SELECT,UPDATE,DELETE语句,表看起来是空的。

你只有在没有会话绑定了临时表时可以执行DDL语句(ALTER TABLE,DROP TABLE,CREATE INDEX)。临时表在会话对其执行INSERT时和会话绑定,在使用TRUNCATE、会话终止或者对事务型临时表使用COMMIT和ROLLBACK表时解除绑定。

事务末尾释放事务型临时表的段,会话末尾释放会话型临时表的段。

父子事务

事务型临时表可以被用户事务和他们子事务访问。但是,一个给定的事务型临时表不能被一个会话中两个事务同时访问,虽然它可以被不同会话中的事务访问。

如果一个用户事务对临时表执行了INSERT语句,然后其所有子事务都不能使用这个临时表。

如果一个子事务对临时表执行INSERT语句,然后在这个子事务的末尾,临时表的数据会消失。然后用户事务或者子事务可以访问临时表。

外部表

外部表访问外表资源的数据就如同它们是数据库中的表一样。你可以连接目标数据库并使用DDL(数据定义语言)创建元数据外部表。外部表的DDL由两个部分组成:一部分描述了Oracle列类型,另一部分(访问参数)描述了外部数据到Oracle数据列的映射。

一个外部表不用于任何保存在数据库内部的数据。也不描述数据如何保存在外部资源中。相反,它描述了外部表层如何展现数据给服务器。外部表只是一个数据文件上的数据需要的访问驱动和外部表层的必要的转换,这样数据才能符合外部表定义。

外部表是只读的;因此不允许任何DML操作,不能创建任何索引。

访问驱动

外部表环境中,数据装载指的是从外部表读取数据,然后装载到数据库内的表的动作。数据转储指的是从数据库中表读取数据和插入到外部表中。

默认的外部表类型是ORACLE_LOADER,这让你可以可以从外部表读取数据并装载进数据库。Oracle还提供ORACLE_DATADUMP类型,这允许你转储数据(就是说,从数据库的表中读取数据,然后插入到外部表中),然后在数据库中重新装载它。

外部表的定义使得数据源中的数据和定义分离,这样意味着:

源文件包含的列可以多余或少于外部表中的列

数据源中域的数据类型可以和外部表定义的列类型不同

使用外部表装载数据

外部表的主要作用是作为一个行来源将数据装载到数据库的一个实际表中。你创建外部表之后,你可以使用CREATE TABLE AS SELECT或者INSERT INTO ..... AS SELECT语句,使用外部表作为SELECT语句的来源。

注意:你不能向外部表插入或者更新其中的数据;外部表是只读的。

当你通过SQL语句访问外部表,外部表的域可以像正常表任何其他域一样使用。特别的是,你可以将域作为任何SQL内建函数、PL/SQL函数或Java函数的参数。这样你就可以处理外部数据的资源。对于数据仓库来说,你可以使用简单的类型转换来进行更多的复杂转换。你可以在数据库中使用这种机制来进行数据清理。

外部表的并发访问

一个外部表的元数据创建之后,你可以直接或者并行的方式使用SQL访问外部表。这样外部表就像一个视图一样,你可以使用外部表运行任何SQL而不需要将外部数据装入数据库。

外部表的并行访问水平使用标准的并行提示(HINT)和PARALLEL子句。在外部表上使用并发可以并发的访问组成外部表的数据文件。单独文件是否可以并发访问依赖于访问驱动程序的实现和正在访问的数据文件属性(例如,记录格式)。

视图概述

视图是一个或多个表或者其他视图的数据的定制展示。一个视图将查询的输出作为一个表。因而,一个视图可以被认为是一个存储的查询或虚拟表。你可以在大部分可以使用表的地方使用视图。

例如,employees表拥有几个列和一些行的信息。如果你想只使用其他的5列或者指定的行,然后你就创建这张表的视图,这样其他用户就可以直接访问。

图5-5显示了一个叫做staff的视图,它来源于基表employees,需要注意的是视图只显示基表的5列数据。

图5-5 一个视图实例

因为视图来源于表,所以它们之间有类似的地方。例如,你可以像表一样定义1000个列的视图。你可以查询视图,在某些限制下还可以更新、插入和删除视图中的数据。视图上执行的操作实际上影响的是视图基表的数据,并且服从基表的完整性约束条件和触发器。

你不能直接在视图上显示定义触发器,但你可以在视图引用的基表上定义。Oracle还支持视图上的逻辑约束。

视图如何存储

和表不同,视图并不分配存储空间,也不包含实际数据。相反,视图是一个查询的定义,是从视图引用的表中抽取数据的查询。这些表叫做基表(base table)。基表可以是表、也可以是视图(或者实体化视图)。因为视图基于其他对象,视图只需要在数据字典中存储视图的定义(存储的查询),而不需要其他的存储。

视图如何使用

视图提供了一种针对基表的数据进行不同的展现方式。视图是非常强大的,因为它可以针对不同的用户调整展现。视图经常用在:

提供额外的安全性,预先限制了只能访问表的某些行或某些列

例如图5-5显示了STAFF视图没有显示基表employees的salary或commission_pct列

隐藏数据复杂性

例如,一个视图可以使用join定义,从多个表中对关联的列或行进行聚集。但是,视图隐藏了这个信息是来源于多个表的事实。

简化用户的语句

例如,视图允许用户从多个表中查询数据却并不用知道如何执行关联(join)

将应用程序和基表的定义隔离

例如,如果一个视图引用了一个四列表的三列,这时候这个表增加了一个新列,然后视图定义不受影响,使用视图的应用程序不受影响。

表示一些必须使用视图的查询

例如,一个视图可以定义为关联一个表和一个group by 视图,或者视图可以定义为一个表和union视图

保存复杂查询

例如,一个查询可能对表信息进行大量的计算。通过将查询保存为视图,你可以在查询视图时每次都执行计算。

视图的机制

Oracle将定义视图的查询的文本保存在数据字典中。当你在SQL语句中引用视图时,Oracle:

1、将引用视图定义的查询合并到语句中

2、在一个SQL共享区域解析合并的语句

3、执行语句

只有在共享SQL区域中不包含类似的语句,Oracle会在新的共享SQL区域中执行引用视图的语句。因而当你使用视图时,你通过共享SQL(视图的SQL)可以降低内存消耗。

视图参数支持国际化

当Oracle评估包含国际化支持的参数(如TO_CHAR,TO_DATE和TO_NUMBER)文字字符或者SQL函数时,Oracle使用会话默认支持的国际化参数。你可以通过在视图定义中指定国际化支持的参数来覆盖默认值。

使用针对视图的索引

Oracle通过将视图定义的查询合并到原始查询中来确定是否使用视图针对的查询的索引。

考虑下列视图:

CREATE VIEW employees_view AS 

  SELECT employee_id, last_name, salary, location_id

    FROM employees JOIN departments USING (department_id)

    WHERE departments.department_id = 10; 

现在看看下列用户发布的SQL:

SELECT last_name 

  FROM employees_view 

  WHERE employee_id = 9876; 

Oracle处理好的查询结构如下:

SELECT last_name 

  FROM employees, departments

  WHERE employees.department_id = departments.department_id AND 

        departments.department_id = 10 AND

        employees.employee_id = 9876; 

在尽可能的情况下,Oracle将视图定义的查询和任何潜在的视图定义的查询都合并起来。Oracle优化合并的查询就好像你没有引用任何视图的查询一样。因而,Oracle可以使用任何引用的基表列的索引,不管是视图定义引用的列,还是针对视图的查询都可以。

某些情况下,Oracle不能将视图定义和用户提交的查询合并,这时候,Oracle就不能使用引用列的所有索引了。

依赖和视图

因为视图是一个引用其他对象(表、实体化视图或其他视图)的查询的定义,所以视图依赖它引用的对象。Oracle自动处理视图依赖。例如,如果你删除了视图的一个基表然后重新创建了它,Oracle会检查新的基表是否适合已有的视图定义。

可更新关联视图

关联视图指的是在视图定义的FROM字句中包含多个表或视图,而且没有使用任何子句:DISTINCT,聚集,GROUP BY,START WITH,CONNECT BY,ROWNUM和并集操作(UNION ALL,INTERSECT等等)。

可更新关联视图是包含多个表或视图允许执行UPDATE、INSERT、DELETE操作的关联视图。数据字典视图ALL_UPDATE_COLUMNS、DBA_UPDATEABLE_COLUMNS和USER_UPDATE_COLUMNS包含的信息指明了视图的哪些列是可以更新的。为支持可更新属性,视图不能包含任何下列结构:

并集操作符

DISTINCT操作符

聚集或者分析函数

GROUP BY、ORDER BY,CONECT BY或者START WITH子句

SELECT列表中的集合表达式

SELECT列表的子查询

关联(某些特殊情况)

不可更新的视图可以使用INSTEAD OF触发器修改。

对象视图

在Oracle对象关系数据库中,有一种对象视图让你可以获取、更新、插入和删除关系型数据,就好像它作为一个对象类型保存一样。你还可以定视图的类型为对象数据类型,比如object、REF和聚集(嵌套表和VARRAYS)。

内联视图

内联视图不是模式对象。它是一个使用别名(关联名)的子查询,你可以在SQL语句中像视图一样使用它。

实体化视图概述

实体化视图一种用来汇总、计算、复制和分发数据的模式对象。它们很适合多种计算环境,比如数据仓库、决策支持、分布式或移动计算:

在数据仓库中,实体化视图用来计算和保存聚集数据,比如总数和平均值。这些环境的实体化视图是典型的汇总数据,因为它们保存着汇总的数据。它们可以用来使用聚集关联或者非聚集关联。如果是Oracle 9I或更高版本,实体化视图可以支持包含过滤条件的查询。

优化器可以自动识别实体化视图是否适合查询的需要,如果适合的话会使用实体化视图来提供查询性能。优化器透明的使用实体化视图重写请求。查询然后直接指向实体化视图,而不是指向底层的表或视图。

在分布式环境中,实体化视图是用来在分布式站点之间复制数据和在多个站点之间同步更新的冲突解决方案。实体化视图提供了远程站点的数据在本地访问的复制品。

在移动计算环境中,实体化视图用来从中心服务器下载数据的子集到移动客户端,还提供从服务器的定期更新,并且可以将客户端的改变传回中心服务器。

实体化视图在一些方面和索引很像:

它们消耗存储空间

主表数据改变时,它们必须被刷新

它们可以使用查询重写来提高SQL执行性能

对于SQL应用程序和用户来说完全透明

和索引不同的是,实体化视图可以直接使用SELECT子句访问。依赖需要的不同的刷新类型,它们可以直接使用INSERT、UPDATE、DELETE语句访问。

在视图上定义约束

数据仓库应用程序在Oracle数据库中通过关系模式的引用完整性约束(RI constraints)来标识维度数据。引用完整性约束代表表之间的主键和外键约束。通过查询Oracle数据字典,应用程序可以识别引用完整性约束,因而识别数据库中的多维度数据。在某些环境下,数据库管理员基于安全或模式复杂性的考虑,会在维度表和事实表上定义视图。Oracle提供了约束视图的能力。通过允许视图之间的完整性定义,数据库管理员可以将基表的约束传递到视图上,因而允许应用程序在严格限制的环境中识别多维度数据。

视图上只能定义逻辑约束,逻辑约束只是声明而不强制的约束。这些约束的目的不是为了强制任何商业规则,而是为了标识多维度数据。下列约束可以定义在视图上:

主键约束

唯一约束

引用完整性约束

对于一个给定的视图,有效约束状态有声明(declarative)、失效(DIABLE)和无效(NOVALIDATE)。但是也允许RELY或NORELY状态,因为视图上的约束可以用来启动更精密的查询重写;一个视图约束为RELY状态会在重写完整性级别设置为trusted模式时启动查询重写。

注意:虽然视图约束定义是在视图上定义,在视图上操作的,但是本质上从属于底层基表的约束完整性定义,视图的约束可以通过基表的约束强制执行。

刷新实体化视图

Oracle维护实体化视图的方式是在它们的主表修改发生之后刷新实体化视图。刷新方式可以是增量的(fast refresh)或者完全(complete)。对于使用快速刷新模式的实体化视图,实体化视图日志或者直接装载日志保存了主表的修改记录。

实体化视图可以根据命令和一定时间间隔周期来刷新。在同一个数据库中,实体化视图可以在主表的事务提交改变的时候是实时刷新。

实体化视图日志

一个实体化视图是一个记录主表数据修改记录的模式对象,所以定义在主表上的数据可以增量刷新。

每个实体化视图日志和一个主表相关联。实体化视图日志和主表位于同一个数据库同一模式下。

维度概述

一个维度定义多列或列集合的继承关系(父/子)。子级别的每个值和父级别有且只有一个值关联。继承关系是从继承的一个级别到继承的下一个级别的函数依赖。维度是列之间的逻辑关系的容器,而且它不需要分配任何数据存储空间。

CREATE DIMENSION语句指定:

多个LEVEL子句,每个代表维度中的一列或列集合

一个或多个HIERARCHY子句代表临近级别的父子关系

可选ATTRIBUTE子句,每个代表单独级别的关联列或列集合

维度中的列来自于同一个表(非规泛化的)或者来自于多个表(完全或部分规泛化)。在多个表上的多个列上定义维度,使用HIERARCHY子句的JOIN子句来连接数据库。

序列生成器概述

序列生成器提供了一个数列的生成顺序。序列生成器在多用户环境中生成唯一顺序数字时非常有用,而不会增加磁盘I/O或事务锁的负载。例如,假设两个用户同时对employees插入新的employee行。通过使用序列来针对employee_id列生成唯一雇员号码,用户不需要等待另一个用户就可以插入下一个雇员号码。序列自动为每个用户创建正确的值。

因此,在两个事务的语句必须同时产生序列号时,序列生成器降低了串行度。通过避免多个用户等待其他用户生成和等待序列号造成的串行化,序列生成器提高了事务吞吐量,单个用户的等待会尽可能的短。

序列号是Oracle内部定义的长达38字节的整数。一个序列定义展现了基本信息,通常如下:

序列名称

序列升序还是降序

数字之间的间隔

Oracle是否应该在内存中缓存一系列的生成的序列号

Oracle在数据库的SYSTEM表空间的单个数据字典的行保存所有序列的定义。因而,所有序列定义是可见的,因为SYSTEM表空间总是在线。

SQL语句引用了序列就可以使用序列号。你可以发布一个语句来生成一个新的序列号或者使用当前序列号。在用户会话中,一个语句产生一个序列号之后,这个序列号只对这个会话可见。引用序列的每个用户都可以访问当前的序列号。

序列号的创建和表无关。因而,相同的序列号生成器可以给多个表使用。生成序列号对你在数据上自动产生主键和在多个表或多个行产生相同的键值。如果序列号在事务中生成、使用且最后回滚了,序列号就会产生跳跃。如果想的话,应用程序可以预作安排来捕捉和重用这些序列号。

注意:如果你的应用程序从来不丢失序列号,或者你不能使用Oracle序列,你可以选择在数据库表中保存序列号。使用数据库表实现序列生成器要非常小心。即使在单实例情况下,对于高频率的序列值生成,保存序列值的行的锁消耗可能是个性能瓶颈。

同义词概述

同义词是一个对象的别名,对象可以使任何表、视图、实体化视图、序列、过程、函数、包、类型、Java类模式对象、用户定义的对象类型或者其他的同义词。因为同义词只是一个别名,它只需要在数据字典中定义而不需要其他存储空间。

同义词经常基于安全和方便原因采用。例如,它们可以达到如下目的:

隐藏对象的名字和拥有者

为分布式数据库的远程对象提供本地透明性

为数据库用户简化SQL语句

可以对精细权限控制的专用视图提供有限度的访问

你可以创建公用和私用同义词。一个公用同义词的拥有者是叫做PUBLIC的特定用户组,数据库中的每个用户都可以访问这个同义词。私用(private)同义词在指定用户的模式下,对其他用户的访问可以进行控制。

同义词在分布式数据库环境和非分布式数据库环境中都非常有用,因为它隐藏了底层对象的标识,还包括在分布式系统中的位置。同义词有很大的优点,因为如果底层对象被重命名或者移动了,只需要重新定义同义词就可以了。基于同义词的应用程序可以正常工作而不需要做任何修改。

同义词可以简化分布式数据库环境中的用户的SQL语句。下面的例子显示了数据库管理员经常创建公共同义词来隐藏基表的标识和降低SQL语句复杂性的方法以及原因。假定下列情况:

用户JWARD拥有的模式有一个叫做SALES_DATA的表

表SALES_DATA的SELECT权限赋予了PUBLIC

这样的话,你不得采取类似的SQL语句来查询SALES_DATA表:

SELECT * FROM jward.sales_data;

你可以注意到必须一起使用表名和包含表名的模式来执行查询。

假定数据库管理员使用如下SQL语句创建一个公共同义词:

CREATE PUBLIC SYNONYM sales FOR jward.sales_data;

创建公共同义词之后,你可以使用一个简单的SQL语句查询表SALES_DATA:

SELECT * FROM sales;

可以注意到公共同义词SALES隐藏了表SALES_DATA的名称和包含它的模式的名称。

索引概述

索引是表和聚集表可选的关联结构。你可以在表的一个或多个列上创建索引来加快其上的SQL语句执行速度。就如同使用本手册的索引比没有索引可以帮你快速定位信息一样,一个Oracle索引提供了对于表数据更快的访问方法。正常情况下,索引是降低磁盘I/O的主要方法。

你可以在表上创建很多索引,可以使用列的任意不同组合来创建索引。你可以在相同的行上创建多个索引,只要列的组合顺序不同就可以了。例如,下列语句指定了有效的组合:

CREATE INDEX employees_idx1 ON employees (last_name, job_id); 

CREATE INDEX employees_idx2 ON employees (job_id, last_name); 

Oracle提供了多个索引模式,提供了针对不同情况下的性能优化:

B树索引

B树聚集索引

Hash聚集索引

反向键值索引

位图索引

位图联结索引

Oracle还支持函数索引和指定应用程序或使用范围的域索引。

增加或者减少索引并不需要修改任何的SQL语句的语法。索引只是数据的快速访问途径。它只影响执行速度。假定一个数据被索引,索引就会直接指向包含这个值的行的位置。

索引在逻辑上和物理上都和关联表的数据独立。你可以任何时候创建和删除索引而不会影响基表以及其他的索引。如果你删除索引,所有的应用程序可以继续运行。但是,以前访问删除的索引的数据会降低速度。索引是一个独立的结构,还是需要存储空间的。

索引创建后Oracle自动维护和使用索引。不需要用户额外操作,Oracle会自动将数据改变(比如增加行、修改行或删除行)反映到相关索引上。

索引数据的获取性能基本保持一致,甚至增加了新行也是如此。但是一个表上存在多个索引还是会降低更新、删除和插入的性能,因为Oracle必须更新表的相关索引。

优化器可以使用现存的索引来构建另一个索引。这样会创建一个更快的索引。

唯一和非唯一索引

索引可以是唯一或者非唯一的。唯一索引保证表在索引列(一个或多个)没有重复的行值。非唯一索引并不强迫限制列值(唯一)。

Oracle推荐显示的使用CREATE UNIQUE INDEX语句来创建唯一索引。通过主键或唯一约束来创建唯一索引并不一定会创建新索引,而且创建的索引也不确保是唯一索引。

复合索引

复合索引(也叫关联索引)是一个在一个表的多个列上创建的索引。复合索引的列可以是任何顺序的,并且不需要在表中相邻。

在SELECT语句的WHERE子句引用了复合索引的全部或者起始列的情况下,复合索引可以加快获取数据的速度。因而,定义中使用的列的顺序非常重要。通常,最常访问或最高选择率的行放在前面。

图5-6说明了VENDOR_PARTS表在VENDOR_ID和PART_NO列上有一个复合索引。

图 5-6 符合索引示例

通常一个复合索引不能超过32个列。对于位图索引来说,列数最大数目为30。一个键值大致不能超过一个数据块的可用空间的一半(多个限制的最小值)。

索引和键

虽然这个两个概念经常可以互换,但索引和键还是有区别的。索引是保存在数据库中的物理存储的结构,用户可以使用SQL语句创建、修改和删除索引。创建索引是为了提供一种的更快的访问表数据的方式。键严格来说是一种逻辑概念。键对应于Oracle叫做完整性约束的这个特性,这个约束在数据库中强制了商业规则。

因为Oracle使用索引来强制一些完整性约束,键和索引经常互换的使用。但是,不要对它们彼此产生混淆。

索引和NULL

索引中的NULL值一般认为是不同的,除非索引中的2个或者更多的非NULL值行是一样的,这种情况下,这些行被认为是相等的。因而UNIQUE索引不允许重复值,所以索引不包含NULL值(即NULl值不索引)。如果整个行全为空,索引不会起作用。

Oracle不对所有键值为NULL的列索引,除非是在位图索引或聚集键列值为NULL的情况下。

函数索引

你可以针对包含表中一个或多个列的函数和表达式建立索引。一个函数索引计算函数或表达式的值并保存在索引中。你可以创建B树函数索引,也可以创建位图函数索引。

构建索引的函数可以是数学表达式,也可以包含PL/SQL函数、包函数、C标注、SQL函数。表达式不能包含任何聚集函数,并且必须是有确定值的。对在需要构建索引的行包含的对象类型,函数可以是该类型的一个方法,比如映射方法。但是,你不能在LOB、REF或嵌套表列构建函数索引,而且如果对象类型包含LOB、REF或嵌套表的话,也不能构建函数索引。

函数索引的使用

函数索引为评估WHERE子句包含函数的语句提供了一个高效机制。表达式值是计算和保存在索引中的。当你执行INSERT和UPDATE语句时,Oracle也必须计算这个函数来执行这个语句。

例如,如果你创建下列索引:

CREATE  INDEX  idx  ON  table_1 (a+b*(c-1),a,b);

然后Oracle在处理下列查询时可以使用它:

SELECT  a  FROM  table_1  WHERE  a+b*(c-1)<100;

UPPER(column_name)或者LOWER(column_name)定义的函数索引可以加快大小写不敏感的查询速度。例如,下列索引:

CREATE  INDEX  uppercase_idx  ON  employees  (UPPER(first_name));

可以在处理下列查询时使用:

SELECT * FROM employees WHERE UPPER(first_name) = 'RICHARD'; 

基于函数索引可以提供国际化支持的排序索引,这样可以在SQL语句中提供高效的多语言校订。

函数索引的优化

你必须对优化器收集函数索引的统计信息。否则索引无法用来执行SQL语句。

优化器可以对WHERE子句的表达式使用函数索引的索引范围扫描。例如,在这个查询中:

SELECT * FROM t WHERE a + b < 10; 

如果构建了a+b这样的索引,优化器可以执行索引范围扫描。索引扫描路径在断语(WHERE子句)有低选择率时非常有用。另外,如果表达式在函数索引中实现了,优化器可以更准确的评估短语包含的表达式的选择率。

优化器通过解析SQL语句来执行表达式匹配,然后比较语句的表达式树和函数索引。比较是不分大小写和忽略空格的。

函数索引的依赖

函数索引依赖于定义索引的表达式使用的函数。如果函数是PL/SQL函数或者包函数,函数定义的任何改变都会将索引失效。

创建一个函数索引,用户必须有CREATE INDEX或CREATE ANY INDEX权限。

想要使用一个函数索引:

索引创建之后表必须分析过

查询必须确保索引表达式不包含任何NULL值,因为NULL值没有保存在索引中。

下面部分描述了额外的需求。

函数确定性

任何用户自定义的函数想使用到函数索引必须采用DETERMINISTIC关键字来声明,指出函数无论何时对于任何给定的输入参数值,函数会产生相同的输出。

定义函数的权限

索引拥有者需要函数的EXECUTE权限才能在函数索引上使用这个函数。如果EXECUTE权限被撤销,Oracle将索引标识为无效(DISABLE)。索引拥有者不需要函数的EXECUTE WITH GRANT OPTION权限来分配基表的SELECT权限。

解决函数索引的依赖关系

函数索引依赖于任何它使用的函数。如果函数或包含这个函数的包重新定义了(或者如果索引拥有者的EXECUTE权限被取消),然后会发生下列情况:

索引被标记为无效(DISABLE)

如果优化器选择使用的索引被标记无效,当前涉及这个查询的查询会失败

标记为DISABLE的索引上的DML操作也会失败,除非索引是标记为UNUSABLE和初始化参数SKIP_UNUSABLE_INDEXES标记为真(true)。

函数修改之后想要重新生效索引,可以使用ALTER INDEX ... ENABLE语句。

索引如何存储

当你创建一个索引时,Oracle在表空间上自动分配一个索引段来保存索引数据。你可以使用下列方式控制索引段的空间分配和备用空间的使用:

设置索引段的存储参数来控制索引段的区段的分配

设置索引段的PCTFREE参数来控制组成索引段的区段的数据块的空闲空间

索引段的表空间可以是拥有者的默认表空间或者CREATE INDEX语句指定名字的表空间。你不必将索引放在和关联表相同的表空间中。此外,你可以通过将索引和表放在位于不同磁盘驱动器的不同表空间上可以提高使用索引的查询速度,因为Oracle可以并发的获取索引和表的数据。

索引块的格式

索引数据的可用空间是Oracle块大小减去块首部、条目首部、rowid、和每个索引值的一个长度字节。

当你创建索引时,Oracle将索引列取出和排序,并把每一行的索引值和rowid一起保存。然后Oracle从下往上装载索引。例如,考虑如下语句:

CREATE INDEX employees_last_name ON employees(last_name); 

Oracle将employees表根据last_name排序。然后按照排序顺序来装载last_name和对应的rowid值到索引上。当它使用这个索引时,Oracle对排序的last_name值快速扫描,然后使用查询到的last_name值关联的rowid值来定位行。

索引的内部结构

Oracle使用B树索引来加快数据访问。没有索引的情况下,你不得不依次扫描数据来找到值。对于n行来说,平均的查询行数为n/2。这个不会随着数据量的增加而缩放。

考虑将一个值的排序列表分成多个块宽度的范围(页块)上。块的末尾除了块的指针之外都可以保存在一个搜索树中,n条数据花费log(n)的时间就可以找到。这就是Oracle索引隐含的基本原理。

图5-7 B树索引的内部结构

B树索引的较高的块(树枝块)包含指向低级别索引块的索引数据。最低级别的索引块(叶块)包含每个索引数据和用来定位实际行的对应的rowid。页块是双向关联的。索引列的包含的字符集数据是基于数据库的字符集的二进制字符。

对于唯一索引,每个数据值对应一个rowid。对于非唯一索引,排序序列中包含rowid,所以非唯一索引通过索引键和rowid排序。包含null的所有键值不索引,除非是聚集索引。两行都包含null,不违反唯一约束(因为Null是互不相同的)。

索引属性

有两种类型的块:

搜索使用的树枝块

页块保存的值

树枝块

树枝块保存下列内容:

两个键之间需要用来分叉的最小键前缀

指向包含键值的子块

如果块拥有n个键,,那么它们包含n+1个指针。键和指针的数量由块大小限制。

页块

所有的页块到根块的高度都相同。页块保留下列信息:

每行的全部键值

表行的ROWID

所有的键和ROWID对都是左右相连的。他们按照(key,ROWID)排序。

B树结构的优势

B树结构有如下优势:

树的所有页块高度相同,所以获取索引上任何地方的任何数据所需的时间大致相同

B树索引自动保持均衡状态

B树索引的所有块平均来说是3/4满的

B树对大多数查询提供了优秀的检索性能,包括精确识别和范围搜索

插入、更新和删除都是高效的,并维护键顺序以便快速的获取数据

B树索引对于小表和大表同样优秀,并且不会随着表尺寸的增长而降低性能

索引唯一扫描

索引唯一扫描是最高效的访问数据方式之一。这个访问方式用来从B树索引返回数据。在B树唯一索引的所有行指定相等条件时,优化器会选择索引唯一扫描。

索引范围扫描

索引范围扫描是访问选择性数据的通常操作。它可以是有限制的(两边都有界限)或者无限制的(在一边或者两边)。数据按照索引列的升序返回数据。值相同的多个行按照ROWID升序保存。

键压缩

键压缩使你可以压缩索引或索引组织表的主键列值的一部分,这样可以减少重复数据造成的存储瓶颈。

通常,索引中键分为两类,一类是分组键,一类是唯一键。如果键没有定义为唯一键,Oracle将rowid加到分组键中。键压缩是一种减少分组键,并保存它的方法,这样可以由多个唯一键共享。

条目的前缀和后缀

键压缩将索引键分为前缀条目(分组部分)和后缀条目(唯一部分)。压缩通过在索引块的后缀条目之间共享前缀条目来获得。只有B树索引的叶块能够压缩。枝块的键后缀可以被截断,但是键不能被压缩。

键压缩发生在一个索引块内,不会跨多个索引块。后缀条目组成了索引行的压缩版本。每个后缀条目引用一个前缀条目,它和后缀条目保存同一个索引块中。

默认情况下,由所有键值列组成的前缀不包括最后一个。例如,在一个由三个列(column1,column2,column3)组成的键中,默认的前缀是(column1,column2)。对于一系列值(1,2,3), (1,2,4), (1,2,7), (1,3,5), (1,3,4), (1,4,4),重复出现的(1,3),(1,2)前缀被压缩。

作为可选方案的是,你可以指定索引长度,索引长度是前缀列的数目。如果指定了前缀长度为1,然后前缀是column1,后缀是(column2,column3)。对于下列值(1,2,3), (1,2,4), (1,2,7), (1,3,5), (1,3,4), (1,4,4),前缀中重复发生的1被压缩。

非唯一索引的最大前缀长度是键值列的数目,唯一索引的最大前缀长度是键值列的数目减1。

只有在索引块中不包含相等的前缀条目才会将前缀条目写入索引块。前缀条目在写入之后就立刻可以共享,并且保持到最后删除的引用后缀条目被清理出索引块为止。

性能和存储考虑

键要所会节省大量的空间,让你可以在每个索引块上保存更多的键,这样会导致更少的I/O和更好的性能。

虽然键压缩减少了索引需要的存储空间,它却提高了在索引扫描时重构键值时需要的CPU时间。它还会导致额外的空间消耗,因为每个前缀条目对应有4个字节的消耗。

键压缩的使用

键压缩在某些情况下非常有用,如:

在通常的非唯一索引中,Oracle将rowid加到键上来确保唯一行。如果使用键压缩,Oracle将除rowid的重复键作为前缀条目保存在索引块中。剩下的后缀条目只由rowid组成。

同样的情况可以在键值组成为(item,time,stamp)的唯一索引中哦个看到。例如(stock_ticker, transaction_time)。上千行可能拥有相同的stock_ticker值,通过transaction_time保持唯一。在特定的索引块,一个stock_ticker值只作为前缀条目保存一次。索引中其他的条目是transaction_time值,可以作为后缀条目保存,引用通用的stock_ticker前缀条目就可以了。

在包含数据类型为VARRAY或者NESTED_TABLE的索引组织表中,对象标识符对于每个聚集数据类型都是重复的。键压缩是你可以压缩重复的对象标识符。

在某些情况下,键压缩无法使用。例如,在只包含一个属性键的唯一索引,键压缩就不可能,因为只有唯一的部分,不存在分组的部分来共享。

反向键索引

和标准索引不同,创建一个反向键索引在保持列顺序的同时反转索引列(除rowid之外)的字节顺序。这样处理可以避免RAC环境中修改的索引集中在页块的一小部分上造成的性能降低。通过反转索引的键值,插入会在索引中分布跨越所有的叶。

使用反转键方法会降低在索引上运行索引范围扫描查询的能力。因为词典临近的键值在反转键索引中并没有保存在一起,只有按键值取数据或者全索引(表)扫描能够执行。

有时候,使用反转键索引能够使得OLTP真正应用集群(RAC)运行的更快。例如,在一个e-mail应用程序中保持邮件信息的索引:某些用户保留旧消息,索引必须象最近的消息一样维护指向这些消息的指针。

REVERSE关键字提供了一种创建反向键索引的简单机制。你可以在CREATE INDEX语句中可选的索引定义中指定REVERSE关键字。:

CREATE INDEX i ON t (a,b,c) REVERSE; 

你可以指定关键字NOREVERSE来将一个反转键索引重建为非反转键索引:

ALTER INDEX i REBUILD NOREVERSE; 

不使用NOREVERSE关键字重建一个反转键索引会重建一个反转键索引。

位图索引

索引的目的是为表中包含一个给定键值的行提供一个访问指针。在通常的索引中,通过针对每个键保存包含这个键值的行的rowid达到这种效果。Oracle针对每个rowid重复的保存键值。对于位图索引来说,每个键值使用一个位图,在通常索引中,每个键值对应一系列的rowid。

位图的每个位对应一个潜在的rowid。如果设置了位,就意味着对应rowid的行包含这个键值。位图索引虽然采用了不同的内部表现,但是通过一个映射函数将位转换为实际的rowid,这样位图索引提供了通常索引同样的功能。如果不同的键值数量很少,那么位图索引是非常节省空间的。

位图索引针对WHERE子句的多个条件的索引进行高效的合并。符合条件的某些而不是全部的行在访问表之前就被过滤掉了。这常常戏剧化的提高了反应时间。

数据仓库应用程序的受益

位图索引对数据仓库应用程序很有用处,数据仓库拥有大量的数据和特殊的查询,但是只有很低数量级的并发事务。对这样的应用程序,位图索引提供:

降低对于大量特定查询的反应时间

对于其他索引技术可以大量节省空间

在非常低端的硬件上也能获得令人惊异的性能

并发的DML和装载数据非常高效

对于一个大表使用传统的B树索引来全部索引对于空间来说是非常昂贵的,因为索引可能比表中的数据大好几倍。位图索引只占表中被索引数据的一小部分。

位图索引对于包含大量并发修改数据的事务的OLTP应用程序并不适合。这些索引适合数据仓库应用程序的决策支持,这类系统通常查询数据而不是更新数据。

位图索引对于索引列主要是比较大小的情况也不适合。例如,一个经常出现在WHERE子句来对于特定值比较大小的salary列在B树索引下性能更好。位图索引只对相等查询比较有用,尤其是AND、OR、NOT操作符的组合。

Oracle优化器和执行引擎集成了位图索引。其他的Oracle执行方法也与位图索引无缝集成。例如,优化器可以在使用位图索引的表和通常的B树索引的表上执行hash连接。优化器考虑位图索引和其他可用的访问方式(比如通常的B树索引和全表扫描),选择最高效的方式,在适当的时候一起考虑这些方式。

并发查询和并发的DML对于位图索引和传统索引来说是差不多的。分区表中的位图索引必须是本地索引。也支持并行创建索引和创建关联索引。

基数

使用位图索引对于低基数列非常有优势:低基数行就是哪些不同值的数量和表行数比非常小。如果列的不同值的数量少于表行数的1%,或者列值重复100次以上,那么这个列就是位图索引的候选者。即使一列重复次数不多,只要高基数列经常在WHERE子句中包含复杂的条件查询也可以。

例如,一个拥有100万的行,1万个不同值的列适合做位图索引。这列的位图索引比B树索引要好,尤其是这列经常和其他列关联查询时。

B树索引对于高基数列非常高效:就是说拥有很多可能的值的列,例如CUSTOMER_NAME或PHONE_NUMBER。在某些情况下,B树索引可能远远大于索引数据。适当使用的话,位图索引比比对应的B树索引小很多。

在特定查询和类似的条件下,位图索引可以戏剧化的提高查询性能。一个查询中的WHERE子句中的AND和OR条件可以在转换位图结果到rowid之前在位图上直接执行对应的布尔操作来快速获得。如果目标行数较小,查询可以很快返回,而不需要对于全表扫描结果重新排序。

位图索引例子

表5-1显示了一个公司顾客数据的一部分。

表5-1位图索引的例子

CUSTOMER #

MARITAL_ STATUS

REGION

GENDER

INCOME_ LEVEL

101

single

east

male

bracket_1

102

married

central

female

bracket_4

103

married

west

female

bracket_2

104

divorced

west

male

bracket_4

105

single

central

female

bracket_2

106

married

central

female

bracket_3

MARITAL_STATUS,REGION,GENDER和INCOME_LEVEL都是低基数列。婚姻状况(marital status)和地域(region)只有3个可能值,性别(gender)只有2个可能值,收入水平(income level)有4个可能值。因为,这些行适合创建位图索引。一个位图索引不应该创建在CUSTOMER#上,因为这个是高选择性列。相反,在这个列上使用唯一B树索引可以提供更高效的展现和获取效率。

表5-2描述了例子中的REGION列的位图索引。它有三个独立的位图组成,每个针对一个区域

图5-2 位图例子

REGION='east'

REGION='central'

REGION='west'

1

0

0

0

1

0

0

0

1

0

0

1

0

1

0

0

1

0

位图中的每个条目或位都对应CUSTOMER表的一行。每个位的值对应表中对应行的值。例如,在位图REGION='east'第一位是1。因为CUSTOMER表的第一行的region是east。位图REGION='east'其他位都是0,因为这个表的其他REGION列都不包含east行。

任何关于公司客户的人口统计学分析调查都会问:“住在中部或西部区域的已婚顾客有多少?”这对应下列的SQL查询:

SELECT COUNT(*) FROM CUSTOMER  

    WHERE MARITAL_STATUS = 'married' AND REGION IN ('central','west'); 

位图索引可以高效的处理在位图结果中计算数目的查询,图5-8显示了这个情况。为了标识出哪些指定的客户满足条件,位图结果可以用来访问表。

图5-8 使用位图索引运行查询

位图索引和空值

和大多数其他类型的索引不一样,位图索引可以包含NULL值的行。对NULL索引对有些类型的SQL语句非常有用,例如聚集函数COUNT的查询。

分区表的位图索引

和其他索引一样,你可以在分区表上创建位图索引。唯一的限制是位图索引必须是分区表的本地索引,不能是全局索引。只有非分区表才支持全局位图索引。

位图关联索引

除了单个表的位图索引之外,你可以创建位图关联索引,这是一个关联两个或者更多表的位图索引。一个位图关联索引可以显著的减少那些必须通过限制条件关联的数据的容量。对于表中每个列的每个值,位图索引保存了一个或多个表的行的rowid。在数据仓库环境中,关联条件是主键列或者维度表列或者事实表的外键列的内部相等关联。

位图关联索引比实体化关联视图在存储上更有效率,是更好的实体化关联视图的替代方法。这是因为实体化关联视图不能压缩事实表的rowid。

索引组织表概述

索引组织表的存储组织是主键B树索引的变体。通常的表(堆组织表)的数据在无顺序的聚集(堆)中保存,索引组织表的数据保存在主键排序的B树索引结构中。除了在索引组织表的行中保存主键列的值之外,B树的每个索引条目保存了非键值列的值。

在图5-9所示,索引组织表索某种程度上类似于一个通常表和一个在一个或者多个表列上的索引组成的结构,但是不是维护两个单独的存储结构(一个表、一个B树索引),数据库管理员只维护一个单独的B树索引。而且,和索引条目只包含行rowid不同,非键列值也会保存。因而,每个B树索引包含 <primary_key_value, non_primary_key_column_values>.

图5-9 通常表和索引组织表的结构比较

应用程序使用SQL语句处理索引组织表,和通常的表处理方法没什么区别。但是,数据库系统通过操作对应的B树索引来执行所有的操作。

表5-3汇总了索引组织表和通常表的不同之处。

表5-3 比较索引组织表和通常表

通常表

索引组织表

Rowid唯一标识一行,主键是可选的

主键唯一标识一行。主键是必须的

伪列ROWID的物理ID允许构建辅助索引

伪列ROWID的逻辑rowid允许构建辅助索引

通过物理rowid访问

通过逻辑rowid访问

顺序扫描返回所有行

全索引扫描返回所有行

可以和其他表保存在聚集中

不能保存在聚集中

可以包含一个类型为LONG的列和多个LOB类型的列

可以包含LOB列,但不能包含LONG列

索引组织表的优势

索引组织表加快了根据主键或者主键的任何有效前缀查找表行的速度。包含行的B树页块中保存了一个行的非键列,所以避免了额外的块访问。而且,因为行是按照主键顺序保存的,主键或有效前缀的范围扫描只需要最少的块访问。

为更快的访问经常访问的列,你可以使用行溢出段(后面会提到)来将不常访问的非键列从B树索引的页块移到可选的溢出段(堆组织)。这就限制了行在B树索引页块中保存的大小和条目部分,这会产生一个更小的B树和每个页块保存更高数目的行。

拥有主键索引的堆组织表的主键同时保存在表和索引上,索引组织表只在B树索引中保存主键列,而不是重复保存。

因为行是按照主键顺序保存的,所以通过键压缩可以节省额外的存储空间。

基于逻辑rowid(和物理rowid相对应)的主键在索引组织表的辅助索引中效率很高。这是因为对于逻辑的rowid,即使在表重新组织导致表行移动也不会导致辅助索引不可用。同时,通过逻辑rowid来猜测物理rowid,基于索引组织表的辅助索引的访问性能可以和直接访问通常表的辅助索引性能相提并论。

使用行溢出段的索引组织表

B树索引的条目通常很小,因为它们只包含键值和rowid。但在索引组织表中,B树索引条目可能很大,因为它们由整个行组成。这可能会破坏B树索引的密集聚集特性。

Oracle提供了OVERFLOW子句来处理这个问题。所以你可以指定一个溢出表空间,如果需要的话,一行可以可以分成两个部分:一部分保存在索引中,一部分在溢出存储区域段。

索引条目,包含所有的主键列的列值和一个指向行溢出的的物理rowid指针以及可选的一部分非键列。

溢出部分,包括剩余的非键列的列值。

和OVERFLOW一起,你可以使用两个子句PCTTHRASHOLD和INCLUDING来控制Oracle如果确定一行是否保存为两个部分,还包括在行中的哪些非键列分开。使用OCTHRESHOLD,你可以指定一个块大小的百分比的阀值。如果所有的非键列值适合指定大小的限制,行就不会分成两部分。否则,从不能容纳的第一个非键列开始,剩下的非键值列都保存在表的行溢出段中。

INCLUDING子句可以让你指定一个列名,这样在CREATE TABLE语句中在指定列的后面的非键列都保存在溢出段上。不过需要注意的是,由于PCTTHRESHOLD基线的限制,额外的非键列有时候需要保存在溢出段中。

索引组织表的辅助索引

索引组织表的辅助索引提供了对非主键或者非主键的前缀的列访问的高效方式。

Oracle在索引组织表上使用基于表主键的逻辑行标识(逻辑rowid)构建辅助索引。逻辑rowid包含一个物理猜测,这个标识了行的块位置。Oracle可以使用这些物理猜测来直接探测索引组织表的页块,不需要使用主键搜索。因为索引组织表中的行没有持久的物理地址,行可能会移到新的块中,所以物理猜测可能变的陈旧。

对通常表来说,通过辅助索引访问包含辅助索引的一个扫描加上一个取得包含行的数据块的I/O。对于索引组织表来说,访问辅助索引稍有变化,依赖于是否使用物理猜测以及其准确性:

不使用物理猜测,访问包含两个索引扫描:辅助索引扫描跟着一个主键索引扫描

使用精确物理猜测,访问包括一个辅助索引扫描和获取包含行的数据块的额外I/O

使用不精确的物理猜测,访问包含一个辅助索引扫描和一个取得错误数据块的I/O(通过物理猜测指明的),还有一个主键索引的扫描。

索引组织表的位图索引

Oracle支持分区和非分区的索引组织表上的位图索引。在索引组织表上创建位图索引表需要一个映射表。

映射表

映射表是一个堆组织表,包含索引组织表的逻辑rowid。明确一点就是每个映射表行包含对应索引组织表的行的逻辑rowid。因而,映射表提供了索引组织表行的逻辑rowid和映射表行的物理rowid的一一对应。

在索引组织表上的位图索引和在堆表上的位图索引类似,但是索引组织表的位图索引使用的rowid是这些对应于基表的映射表。每个索引组织表有一个映射表,索引组织表上的所有位图索引都使用这个映射表。

在索引组织表和堆组织表中,位图索引都可以使用一个搜索关键字访问。如果找到这个关键字,位图条目就被转化为一个物理rowid。在对组织表的情况下,这个物理rowid就用来访问基表。但是,在索引组织表的情况下,物理rowid用来访问映射表。访问映射表得到一个逻辑rowid。这个逻辑rowid用来访问索引组织表。

虽然索引组织表的位图索引不保存逻辑rowid,但它实际上是逻辑的。

分区的索引组织表

你可以在索引组织表中在列值上使用RANGE、HASH、LIST来分区。分区列是组成主键列的子集。和通常的表一样,本地分区(前缀或非前缀)索引和全局分区(前缀)索引都支持分区索引组织表。

堆组织表和索引组织表的UROWID列的B树索引

UROWID数据类型的列可以保存基于主键的逻辑rowid来标识索引组织表的行。Oracle支持对堆组织表和索引组织表的UROWID类型列进行索引。索引支持在UROWID列的相等比较。但是索引不支持UROWID类型列的不等比较或排序。

索引组织表应用

索引组织表有很多优势:针对主键查询的高性能、高可用概念、减少存储空间,索引索引组织表对下列应用很理想:

在线事务处理(OLTP)

Internet(例如,搜索引擎和门户)

电子商务(例如,电子商店和目录)

数据仓库

分析功能

应用程序域索引概述

Oracle提供了扩展索引来使用在特定的数据类型(如文档、空间数据、图像、视频)上构建索引和使用专业的索引技术。通过扩展索引,你可以总结特定应用程序的索引管理大纲来作为一个indextype(索引类型)模式对象和在表列或对象类型的属性上定义一个域索引(一个特定应用的索引)。扩展索引还对特定应用操作符提供高效的处理。

Cartridge这个应用软件负责控制域索引的结构和目录。Oracle数据库服务器和应用程序交互来创建、维护和搜索域索引。索引结构本身可以保存在数据库中作为索引组织表或者作为外部文件。

聚集概述

聚集是一种可选的保存表数据的方法。聚集是一组共享数据块的表,这些表共享公用的行并经常一起使用。例如,employees和departments表共享了department_id列。当你聚集employees和departments表时,Oracle物理上将employees和departments表的共用department的所有行保存在相同的数据块上。

图5-10显示了你聚集employees和departments表会发生什么:

图5-10 聚集数据表

因为聚集在相同的数据块上保存不同表的关系行,通常使用聚集可以提供下列好处:

减少了聚集表关联的磁盘I/O

改善了聚集表关联的访问时间

在聚集表中,聚集键值是针对特定行的聚集键列的值。不管不同的表包含多少个这个聚集键值,每个聚集键值在聚集和聚集索引中只保存一次。因而,在聚集表中保存关系表和索引数据比非聚集表格式表需要的空间更少。例如,在图5-10中,可以注意到在employees和departments表中都包含相同的聚集键的值的行只保存聚集键一次。

哈希聚集概述

Hash聚集和普通的索引聚集表分组表数据的方式类似(聚集键的索引不是hash函数)。但是聚集索引中保存的行是基于行聚集键值的hash函数。所有包含相同键值的行在磁盘上保存在一起。

Hash聚集在表经常使用相等查询(例如,返回dempartment 10的所有行)的情况下比使用索引表或者索引聚集好一些。对这样的查询,指定的聚集键值是hash的。Hash键值结果直接指向磁盘上保存这一行的区域。

哈希是一种可选的保存表数据来改善数据获取性能的方法。使用哈希的办法,就是创建一个hash聚集,装载数据到聚集中就可以了。Oracle物理上在哈希聚集中保存表行,根据hash函数的结果获取数据。

排序哈希聚集允许应用更快的获取数据,这些数据在它们插入的时候就已经排序了。

Oracle使用一个hash函数来产生的分布数字值叫做hash值,这些值基于特定的聚集键值。哈希聚集的键和索引聚集的键一样可以是单列或者多列(多列集)。为在哈希聚集中找到或保存数据,Oracle对行聚集键值应用hash函数。哈希值结果对应于聚集中的数据块,然后Oracle可以根据发布的语句来读写它们。

哈希聚集是使用索引的非聚集表和索引聚集的替代方案。通过索引表或索引聚集,Oracle使用保存在独立索引上的键值定位表中的行。为找到或保存索引表或聚集表的行,至少要执行两个I/O:

在索引中找到或保存键需要一个或多个I/O

表或聚集中行的读写的另一个I/O

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多