Oracle Database 12c DBA官方手册(第8版)
上QQ阅读APP看书,第一时间看更新

1.3 Oracle逻辑数据库结构

本节将介绍所有主要逻辑数据库结构的重点部分,首先将介绍表和索引,接下来讨论各种可用于定义表列的数据类型。在创建带有列的表时,可在表列上添加限制,或称为“约束(constraint)”。

使用关系数据库管理系统(Relational Database Management System,RDBMS)管理数据的一个原因在于,要协调利用Oracle数据库的安全性和审核特性。本节将回顾一些方法,用于分离用户对数据库的访问或被访问对象对数据库的访问。

本节还将介绍其他许多可由DBA或用户定义的逻辑结构,包括同义词、外部文件的链接和其他数据库的链接。

1.3.1 表

表是Oracle数据库中的基本存储单位。如果没有表,数据库对于企业来说就没有任何价值。无论表的类型是什么,表中的数据总是存储在行和列中,这类似于在电子表格中存储数据。但这种类似性仅限于此。决定在何处存储关键信息时,由于Oracle数据库的可靠性、完整性和可伸缩性带来的数据库表的健壮性,使电子表格成为次要选择。

本节将回顾Oracle数据库中许多不同类型的表,以及它们如何满足组织的大多数数据存储需求。第5章和第8章将详细介绍如何根据特定的应用程序在这些类型的表中做出选择,以及如何管理它们。

1.关系表

关系表是数据库中最常见的表类型。关系表以“堆(heap)”的形式进行组织;换句话说,表中的行没按任何特定顺序存储。在CREATE TABLE命令中,可指定子句ORGANIZATION HEAP来定义以堆的形式组织的表,但因为这是默认的,所以该子句可以省略。

表的每一行包含一列或多列,每一列都有一种数据类型和长度。从Oracle 8开始,列也可以包含用户定义的对象类型、嵌套表或VARRAY。此外,还可以将表定义为对象表。本节稍后将回顾对象表和对象。

表1-1列出了内置的Oracle数据类型。Oracle也支持ANSI兼容的数据类型,表1-2给出了ANSI数据类型和Oracle数据类型之间的映射关系。

表1-1 Oracle内置数据类型

(续表)

表1-2 与ANSI数据类型等价的Oracle数据类型

2.临时表

从Oracle 8i开始,Oracle就支持临时表。之所以称它们为“临时表”,在某种意义上是因为数据存储在表中,而不是存储在表自身的定义中。命令CREATE GLOBAL TEMPORARY TABLE可创建临时表。

只要其他用户具有访问表自身的权限,他们就可以在临时表上执行SELECT命令或DML命令,如INSERT、UPDATE或DELETE。然而,每个用户只能在表中看到自己的数据。当用户截取临时表时,只会从表中删除他们插入的数据。

临时表中有两种不同风格的临时数据:事务持续期间的临时数据和会话持续期间的临时数据。临时数据的寿命由ON COMMIT子句控制,在执行COMMIT或ROLLBACK命令时,ON COMMIT DELETE ROWS负责删除临时表中的所有行,而ON COMMIT PRESERVE ROWS能在超出事务边界后保留表中的行。然而,当用户的会话终止时,临时表中所有的用户行都会被删除。

使用临时表时还有其他一些注意事项。虽然可在临时表上创建索引,但索引中的条目将随数据行一起被删除,这一点与普通表一样。另外,由于临时表中数据的临时特性,Oracle不会为临时表上的DML生成任何重做信息,而会在撤消表空间中创建撤消信息。

3.索引组织表

如同将在后面关于索引的小节中看到的,创建索引可以更有效地找到表中的特定行。然而,这也会带来一些额外的系统开销,因为数据库必须同时维护表的数据行和索引条目。如果表包含的列并不多,而且对表的访问主要集中在某一列上,应怎么做?这种情况下,索引组织表(Index Organized Table,IOT)可能就是正确的解决方案。IOT以B-树索引的形式存储表中的行,其中B-树索引的每个节点都包含作为键的(索引)列以及一个或多个非索引列。

IOT最明显的优点在于只需要维护一个存储结构,而非两个。类似地,表中主键的值只在IOT中存储一次,而在普通表中则需要存储两次。

然而,使用IOT也存在一些缺点。有些表,如记录事件的表,可能不需要主键,或不需要任何键,而IOT则必须有主键。同时,IOT不可以是群集的成员。最后,如果表中有大量的列,并且在检索表中的行时需要频繁地访问许多列,IOT可能就不是最佳解决方案。

4.对象表

从Oracle 8开始,Oracle Database已经支持数据库中许多面向对象的特性。用户定义的类型,以及针对这些对象类型定义的任何方法,都可以无缝地实现Oracle中面向对象(Object-Oriented,OO)的开发项目。

对象表具有自身就是对象或类型定义实例化的行。可通过对象ID(Object ID,OID)引用对象表中的行,这与关系表或普通表中的主键形成对比。然而,与关系表一样,对象表仍可以有主键和唯一键。

例如,假设正从头开始创建一个人力资源(Human Resources,HR)系统,此时可以完全从面向对象的观点来灵活地设计数据库。第一步是通过创建如下类型来定义雇员对象或类型:

    create type PERS_TYP  as object
       (Last_Name         varchar2(45),
       First_Name         varchar2(30),
       Middle_Initial     char(1),
       Surname            varchar2(10),
       SSN                varchar2(15));

在上面的例子中,未创建带PERS_TYP对象的任何方法,但Oracle默认为该类型创建了一个构造函数方法,它与类型自身同名(在本例中就是PERS_TYP)。为创建作为PERS_TYP对象集合的对象表,可使用我们熟悉的CREATE TABLE语法,具体如下:

    create table pers of pers_typ;

为将对象实例添加到对象表,可以在INSERT命令中指定构造函数方法:

    insert into pers
    values(pers_typ('Nickels','Randy','E','Ms.','123-45-6789'));

从Oracle Database 10g开始,如果表由单个对象的实例组成,则不需要构造函数。其简化的语法如下:

    insert into pers values('Confused','Dazed','E','Ms.','123-45-6789');

对PERS_TYP对象实例的引用可作为REF对象存储在其他表中,并且可用于检索PERS表中的数据,而不需要直接引用PERS表本身。

第5章将通过更多示例来介绍如何使用对象实现面向对象设计项目。

5.外部表

Oracle 9i引入了外部表。简单来说,外部表允许用户访问数据源,如文本文件,就如同该数据源是数据库中的表一样。表的元数据存储在Oracle数据字典中,但表的内容存储在外部。

外部表的定义包含两部分。第一部分(也是最熟悉的部分)是从数据库用户的角度观察的表定义。该定义类似于在CREATE TABLE语句中看到的典型定义。

第二部分用于区分外部表和普通表。这是数据库列和外部数据源之间生成映射的位置:数据元素开始于哪些列,列有多宽,以及外部列的格式是字符还是二进制。外部表ORACLE_ LOADER的默认类型的语法实际上等同于SQL*Loader中控制文件的语法。这是外部表的一个优点,用户只需知道如何访问标准数据库表以获得外部文件。

然而,使用外部表也有一些缺点。在外部表上不可以创建索引,也不可以对其执行插入、更新或删除操作。但是,如果考虑到使用外部表加载本地数据库表(如在数据仓库环境中)的优点,这些缺点就微不足道了。

6.群集表

如果经常同时访问两个或多个表(如一个订单表和一个行项明细表),则创建群集表(clustered table)可能是一种较好的方法,它可以改进引用这些表的查询的性能。在具有相关行项(line-item)明细表的订单表中,订单标题信息可与行项明细记录存储在同一个块中,从而减少检索订单和行项信息所需要的I/O数量。

群集表还可减少存储两个表共有的列所需要的空间量,两个表共有的列也称为“群集键值”。群集键值也存储在群集索引中。群集索引操作起来非常类似于传统的索引,通过群集键值访问群集表时,可改进对群集表的查询。在具有订单和行项的示例中,订单号只需要存储一次,不必针对每个行项明细行重复存储。

相对于对表执行的SELECT语句的数量,如果需要频繁地对表执行插入、更新和删除操作,则群集表的优点就会减少。此外,经常对群集中的单个表进行查询也会减弱首先对表进行群集化的优势。

7.散列群集

作为特殊类型的群集表,散列群集(hash cluster)操作起来非常类似于普通的群集表,但是,它不使用群集索引,而使用散列函数来存储并检索表中的行。创建表时,将根据在创建群集期间指定的散列键的数量分配所需要的预估空间。在订单条目示例中,假设Oracle数据库需要镜像遗留的数据条目系统(该系统将周期性地重复使用订单号)。同时,订单号始终是6位数字,那么可按下面的示例创建订单群集:

    create cluster order_cluster (order_number number(6))
        size 50
        hash is order_number hashkeys 1000000;
    create table cust_order (
       order_number      number(6) primary key,
       order_date        date,
       customer_number    number)
    cluster order_cluster(order_number);

使用相等比较方式从表中选择行时,散列群集具有性能优势,如下所示:

    select order_number, order_date from cust_order
        where order_number = 196811;

一般情况下,如果HASHKEYS的数量足够多并且包含散列函数的HASH IS子句产生均匀分布的散列键,那么这种查询在检索行时将只使用一次I/O。

8.排序的散列群集

排序的散列群集是Oracle 10g中的新增内容。它们类似于普通的散列群集,通过使用散列函数来定位表中的行。然而,除此之外,排序的散列群集允许对表中的行根据表的一列或多列进行升序排列。如果遇到进行先进先出(First In First Out,FIFO)处理的应用程序,该方法就可以更快速地处理数据。

可以先创建群集本身,再创建排序的散列群集,但需要在群集中列定义的后面加上SORT位置参数。下面是在排序散列群集中创建表的示例:

    create cluster order_detail_cluster (
        order_number number(6), order_timestamp timestamp)
        size 50 hash is order_number hashkeys 100;
    create table order_detail (
        order_number     number,
        order_timestamp   timestamp sort,
        customer_number   number)
    cluster order_detail_cluster (
        order_number,
        order_timestamp);

基于排序散列群集的FIFO特性,通过order_number访问订单时,将根据order_timestamp的值首先检索时间最久的订单。

9.分区表

对表进行分区或对索引进行分区(下一部分将介绍对索引进行分区)可帮助建立更便于管理的大型表。可将表分区(甚至细分)为较小的部分。从应用程序的观点看,分区是透明的(也就是说,在终端用户的SQL中不需要对任何特定分区进行显式的引用)。用户唯一能够观察到的是,在WHERE子句中使用匹配分区方案的标准对分区表进行查询,将运行得更快。

从DBA的观点看,进行分区有很多优点。如果表的一个分区位于已损坏的磁盘卷上,则在修复遭到破坏的卷时,用户仍可查询表的其他分区。与此类似,对分区的备份可以许多天进行一次,每次备份一个分区,而不需要一次性地对整个表进行备份。

分区大致有3种类型:范围分区、散列分区及列表分区。从Oracle 11g开始,也可以根据父/子关系进行分区,由应用程序控制分区,并可对基本分区类型进行很多组合,包括列表-散列、列表-列表、列表-范围和范围-范围等。分区表中的每一行能且只能存在于一个分区中。分区键为行指示正确的分区,它可以是组合键,最多可组合表中的16列。对可分区的表类型有一些次要的限制,例如,包含LONG或LONG RAW列的表不能进行分区。LONG限制极少会成为问题。LOB(包括字符大型对象CLOB和二进制大型对象BLOB)则灵活得多,包含LONG和LONG RAW数据类型的所有特性。


提示:

Oracle公司建议,对于任何大于2GB的表,应尽量考虑对其进行分区。


无论使用何种类型的分区模式,分区表的每个成员都必须具有相同的逻辑属性,如列名、数据类型和约束等。然而,根据每个分区的大小和在磁盘上的位置,它们的物理属性可能有所不同。关键在于,从应用程序或用户的角度看,分区表必须在逻辑上一致。

范围分区 对于范围分区,它的分区键落在某一范围内。例如,对公司电子商务站点的访问可根据访问日期赋给某个分区,每个季度一个分区。在2012年5月25日对站点的访问将记录在名为FY2012Q2的分区中,而在2012年12月2日对站点的访问则记录在名为FY2012Q4的分区中。

列表分区 在列表分区中,分区键落在完全不同的值组中。例如,按美国国内城市区域划分的销售区域可针对NY、CT、MA和VT创建一个分区,针对IL、WI、IA和MN创建另一个分区。如果缺少州代码,则全球其他区域的销售可赋给各自的分区。

散列分区 散列分区根据散列函数将行赋给分区,只需要指定用于散列函数的一列或多列,不必将这些列显式赋予分区,而只需要指定有多少列可用。Oracle将行赋给分区,并确保每个分区中行的均匀分布。

如果没有明确的列表分区或范围分区模式提供给表中的列类型,或者分区的相对大小经常改变,需要用户重复地手动调整分区模式,则散列分区就非常实用。

组合分区 使用组合分区可对分区进程进一步进行细分。例如,可先对表进行范围分区,然后在每个范围内,使用列表或散列进一步分区。Oracle 11g中新增的组合分区包括列表-散列、列表-列表、列表-范围和范围-范围等分区。

10.分区索引

对表上的索引——或者符合索引表的分区模式(本地索引),或者独立于表的分区模式进行分区(全局索引)。进行分区操作时,本地分区索引可增加索引的可用性,例如,归档并删除分区FY2008Q4及其本地索引不会影响表中其他分区的索引的可用性。

1.3.2 约束

Oracle约束(constraint)是一条或多条规则,它在表的一列或多列上定义,用于帮助实施业务规则。例如,约束可强制实现雇员起薪不得低于$25 000.00这样的业务规则。另一个实施业务规则的约束示例是,如果将新雇员分配到一个部门(虽然不需要立刻将他们分配到特定部门),则该部门号必须有效,并存在于DEPT表中。

共有6种数据完整性规则可应用于表列:空值规则、唯一列值、主键值、引用完整性值、复合内联完整性和基于触发器的完整性。下面将简要介绍这些规则。

创建表或在列的级别上改变表时,定义表上的所有约束(触发器除外,它们是根据在表上执行哪些DML操作来定义的)。可在创建时或将来的任意时间点启用或禁用约束;启用或禁用(使用关键字ENABLE或DISABLE)约束时,根据有效的业务规则,可能需要也可能不需要验证(使用关键字VALIDATE或NOVALIDATE)表中已有数据是否满足约束。

例如,汽车制造商数据库中的CAR_INFO表包含新的汽车数据,需要为其中的AIRBAG_ QTY列添加一个新约束,该列的值不得为NULL,并且对于所有新车辆,该值必须至少为1。然而,该表包含多年前不需要安全气囊的汽车型号的数据,因此AIRBAG_ QTY列包含值0或NULL。这种情况下,一种解决方案是在AIRBAG_QTY表上创建约束,对添加到表中的所有新行强制实施新规则,但不验证已有行是否满足约束。

以下创建的表具有所有约束类型。下面将介绍每种约束。

    create table cust_order
       (order_number          number(6)     primary key,
       order_date             date          not null,
       delivery_date          date,
       warehouse_number       number        default 12,
       customer_number        number        not null,
       order_line_item_qty    number        check (order_line_item_qty < 100),
       ups_tracking_number    varchar2(50)  unique,
       foreign key (customer_number) references customer(customer_number));
1.空值约束

NOT NULL约束可防止将NULL值输入ORDER_DATE列或CUSTOMER_NUMBER列。从业务规则的角度看,这样做很有意义:每个订单都必须有订购日期,而只有在顾客下订单时,订单才有意义。

注意,列中的NULL值并不意味着值为空或0;准确地讲,该值不存在。NULL值不等同于任何内容,甚至不等同于另一个NULL值。在对可能具有NULL值的列使用SQL查询时,这个概念非常重要。

2.唯一列值

UNIQUE完整性约束确保一列或一组列(通过组合约束)在整个表中是唯一的。在前面的示例中,UPS_TRACKING_NUMBER列将不包含重复值。

为强制实施约束,Oracle将在UPS_TRACKING_NUMBER列上创建唯一索引。如果该列上已有一个有效的唯一索引,Oracle将使用该索引来实施约束。

具有UNIQUE约束的列也可声明为NOT NULL。如果没有声明该列具有NOT NULL约束,则任意数量的行都可以具有NULL值,只要剩余的行在该列中具有唯一值。

在允许一列或多列具有NULL值的组合唯一约束中,非NULL值的列用于确定是否满足约束。NULL列总满足约束,因为NULL值不等同于任何内容。

3.主键值

PRIMARY KEY完整性约束是数据库表中最常见的约束类型。一个表上最多只能存在一个主键约束,组成主键的列不能有NULL值。

在前面的示例中,ORDER_NUMBER列是主键。系统将创建唯一索引以实施该约束,如果该列已存在可用的唯一索引,主键约束就使用该索引。

4.引用完整性值

引用完整性或FOREIGN KEY约束比上述任何一种约束都更复杂,因为它依赖于另一个表来限制哪些值可以输入到具有引用完整性约束的列中。

在前面的示例中,在CUSTOMER_NUMBER列上声明外键(FOREIGN KEY);输入该列的值必须也存在于另一个表(在这种情况下是CUSTOMER表)的CUSTOMER_NUMBER列中。

与允许NULL值的其他约束一样,具有引用完整性约束的列可有NULL值,且不需要引用的列包含NULL值。

此外,FOREIGN KEY约束可以自引用。在主键为EMPLOYEE_NUMBE的EMPLOYEE表中,MANAGER_NUMBER列具有根据同一个表中的EMPLOYEE_NUMBER列声明的外键,这就允许在EMPLOYEE表自身中创建一个报告层次结构。

应该总在外键(FOREIGN KEY)列上声明索引以改进性能,该规则的唯一例外出现在绝对不会更新或删除父表中的引用主键或唯一键时。

5.复合内联完整性

通过使用CHECK约束,可在列级别实施更复杂的业务规则。在前面的示例中,ORDER_LINE_ITEM_QTY列不得超出99。

CHECK约束可使用插入或更新的行中的其他列来评估约束。例如,STATE_CD列上的约束只有在COUNTRY_CD列的值不为USA时才允许NULL值。此外,该约束可使用字面值和内置函数,如TO_CHAR或TO_DATE,前提是这些函数能处理字面量或表中的列。

一列上允许有多个CHECK约束。只有在所有的CHECK约束都计算为TRUE时,才允许将值输入列中。例如,可修改前面的CHECK约束,确保ORDER_LINE_ITEM_QTY大于0并小于100。

6.基于触发器的完整性

如果业务规则过于复杂,使用唯一性约束很难实现,则可使用CREATE TRIGGER命令在表上创建一个数据库触发器,同时使用一个PL/SQL代码块实施这一业务规则。

当引用的表存在于不同的数据库中时,需要使用触发器来实施引用完整性约束。触发器也可用于许多超出约束检查领域的情况(例如,对表的审核访问)。第17章将深入介绍数据库触发器。

1.3.3 索引

当检索表中少量的行时,使用Oracle索引能更快访问表中的这些行。索引存储了进行索引的列的值,同时存储包含索引值的行的物理ROWID,唯一的例外是索引组织表(Index-Organized Table,IOT),它使用主键作为逻辑ROWID。一旦在索引中找到匹配值,索引中的ROWID就会指向表行的确切位置:哪个文件、文件中的哪个块,以及块中的哪一行。

可在一列或多列上创建索引。索引条目存储在B-树结构中,因此遍历索引以找到行的键值只需要使用非常少的I/O操作。在唯一索引情况下,使用索引可能有两个目的:提高搜索行的速度,并在索引列上实施唯一或主键约束。在插入、更新或删除表行的内容时,自动更新索引中的条目。删除表时,在该表上创建的所有索引也自动被删除。

Oracle中有一些可用的索引类型,每种索引都适用于特定的表类型、访问方法或应用程序环境。下面将介绍最常见的索引类型的重点内容和特性。

1.唯一索引

唯一索引是最常见的B-树索引形式。它常用于实施表的主键约束。唯一索引确保索引的一列或多列中不存在重复值。可在EMPLOYEE表中Social Security Number(社会保障号)的对应列上创建唯一索引,因为该列中不应有任何重复值。然而,一些雇员可能没有Social Security Number,因此该列可以包含NULL值。

2.非唯一索引

非唯一索引帮助提高访问表的速度,而不会强制实施唯一性。例如,可在EMPLOYEE表的LAST_NAME列上创建非唯一索引,从而提高按姓查找的速度。但对于任何给定的姓,确实可以有许多重复的值。

如果在CREATE INDEX语句中没有指定其他任何关键字,则默认在列上创建非唯一B-树索引。

3.反向键索引

反向键索引(reverse key index)是特殊类型的索引,一般用于OLTP(OnlineTransaction Processing,联机事务处理)环境中。在反向键索引中,反向每列的索引键值中的所有字节。在CREATE INDEX命令中,使用REVERSE关键字指定反向键索引。下面是创建反向键索引的一个示例:

    create index ie_line_item_order_number
             on line_item(order_number) reverse;

如果发出的订单号是123459,则反向键索引将此订单号存储为954321。插入到表中的内容分布在索引的所有叶键上,从而减少一些插入新行的写入程序之间的争用。如果在发出订单后不久就查询或修改订单,则反向键索引也可减少OLTP环境中这些“热点”的可能性。另一方面,尽管反向键索引减少了热点,但它极大地增加了必须从磁盘读取的块数,也增加了索引块拆分的数量。

4.基于函数的索引

基于函数的索引类似于标准的B-树索引,不同之处在于它将被声明为表达式的列的变换形式存储在索引中,而非存储列自身。

当名称和地址可作为混合内容存储在数据库中时,基于函数的索引就非常有用。如果搜索标准是Smith,在包含值SmiTh的列上进行普通索引就不会返回任何值。另一方面,如果索引以全大写字母的形式存储姓,则所有对姓的搜索都可以使用大写字母。下面的示例在EMPLOYEE表的LAST_NAME列上创建基于函数的索引:

    create index up_name on employee(upper(last_name));

因此,使用如下查询的搜索将使用前面创建的索引,而不是进行完整的表扫描:

    select employee_number, last_name, first_name, from employee
         where upper(last_name) = 'SMITH';
5.位图索引

在索引的叶节点上,位图索引(bitmap index)的结构与B-树索引相比存在着较大的区别。它只存储索引列每个可能值(基数)的一个位串,位串的长度与索引表中的行数相同。

与传统索引相比,位图索引不仅可节省大量空间,还可大大缩短响应时间,因为在需要访问表自身之前,Oracle就可以从包含多个WHERE子句的查询中快速删除潜在的行。对于多个位图,可使用逻辑AND和OR操作来确定访问表中的哪些行。

虽然位图索引可用于表中的任何列,但在索引列具有较低基数或大量不同的值时,使用位图索引才最有效。例如,PERS表中的GENDER列将有NULL、M或F值。GENDER列上的位图索引将只有3个位图存储在索引中。另一方面,LAST_NAME列上的位图索引将有和表本身行数基本相同的位图串数量!如果执行完整的表扫描而非使用索引,查找特定姓的查询将很可能花费较少的时间。这种情况下,使用传统的B-树非唯一索引将更有用。

位图索引的一种变体称为“位图连接索引(bitmap join index)”,这种索引在某个表列上创建一个位图索引,此列常常根据相同的列与一个或多个其他的表相连接。这就在数据仓库环境中提供了大量优点,在一个事实表和一维或多维表上创建位图连接索引,实质上等于预先连接这些表,从而在执行实际的连接时节省CPU和I/O资源。


注意:

位图索引只在Oracle 11g和12c的企业版中可用。由于在表上执行DML时,位图索引包含额外的锁定和块拆分开销,因此仅适用于极少更新的列。


1.3.4 视图

视图允许用户查看单独表或多个连接表中数据的自定义表示。视图也称为“存储查询”:用户无法看到视图底层隐藏的查询细节。普通视图不存储任何数据,只存储定义,在每次访问视图时都运行底层的查询。普通视图的增强称为“物化视图(materialized view)”,允许同时存储查询的结果和查询的定义,从而加快处理速度,另外还有其他优点。对象视图类似于传统视图,可隐藏底层表连接的细节,并允许在数据库中进行面向对象的开发和处理,而底层的表仍然保持数据库关系表的格式。

下面将介绍一般数据库用户、开发人员或DBA创建并使用的基本视图类型的基础知识。

1.普通视图

普通视图,通常称为“视图”,不会占据任何存储空间,只有它的定义(查询)存储在数据字典中。视图底层查询的表称为“基表”,视图中的每个基表都可以进一步定义为视图。

视图有诸多优点,它可以隐藏数据复杂性:高级分析人员可以定义包含EMPLOYEE、DEPARTMENT和SALARY表的视图,这样上层管理部门可以更容易地使用SELECT语句检索有关雇员薪水的信息,这种检索表面看来是使用表,但实际上是包含查询的视图,该查询连接EMPLOYEE、DEPARTMENT和SALARY表。

视图也可以用于实施安全性。EMPLOYEE表上的视图EMP_INFO包含除了SALARY(薪水)外的所有列,并且将该视图定义为只读,从而防止更新该表:

    create view emp_info as
        select  employee_number, last_name,
                first_name, middle_initial, surname
    from employee
    with read only;

如果没有READ ONLY子句,则可更新某行或向视图中添加行,甚至可在包含多个表的视图上执行这些操作。视图中有一些构造可防止对其进行更新,例如使用DISTINCT操作符、聚集函数或GROUP BY子句。

当Oracle处理包含视图的查询时,它替换用户的SELECT语句中的底层查询定义,并且处理结果查询,就像视图不存在一样。因此,在使用视图时,基表上任何已有索引的优点并未改变。

2.物化视图

在某些方面,物化视图与普通视图非常类似:视图的定义存储在数据字典中,并且该视图对用户隐藏底层基查询的细节。但相似之处仅限于此。物化视图也在数据库段中分配空间,用于保存执行基查询得到的结果集。

物化视图可用于将表的只读副本复制到另一个数据库,该副本具有和基表相同的列定义和数据。这是物化视图的最简单实现。为减少刷新物化视图时的响应时间,可创建物化视图日志以刷新物化视图。否则,在需要刷新时就必须进行完全的刷新:必须运行基查询的全部结果以刷新物化视图。物化视图日志为以增量方式更新物化视图提供了方便。

在数据仓库环境中,物化视图可存储来自GROUP BY ROLLUP或GROUP BY CUBE查询的聚集数据。如果设置适当的初始参数值,如QUERY_REWRITE_ENABLED,并且查询自身允许查询重写(使用QUERY REWRITE子句),则任何与物化视图执行相同类型的聚集操作的查询都将自动使用物化视图,而不是运行初始的查询。

无论物化视图的类型是什么,在基表中提交事务或根据需要刷新它时,都会自动对其进行刷新。

物化视图在很多方面与索引类似,它们都直接和表联系并且占用空间,在更新基表时必须刷新它们,它们的存在对用户而言实际上是透明的。通过使用可选的访问路径来返回查询结果,它们可以帮助优化查询。

第17章将详细介绍如何在分布式环境中使用物化视图。

3.对象视图

面向对象(OO)的应用程序开发环境日趋流行,Oracle 12c数据库完全支持数据库中本地化对象和方法的实现。然而,从纯粹的关系数据库环境向纯粹的OO数据库环境迁移并非易事,很少有组织愿意花费时间和资源从头开始构建新的系统,而Oracle 12c使用对象视图使这种迁移变得更为容易。对象视图允许面向对象的应用程序查看作为对象集合的数据,这种对象集合具有属性和方法,而遗留系统仍可对INVENTORY表运行批处理作业。对象视图可以模仿抽象数据类型、对象标识符(OID)以及纯粹的OO数据库环境能够提供的引用。

与普通视图一样,可在视图定义中使用INSTEAD OF触发器来允许针对视图的DML,这里使用的是PL/SQL代码块,而非用户或应用程序提供的实际DML语句。

1.3.5 用户和模式

有权访问数据库的数据库账户称为“用户”。用户可存在于数据库中,而不拥有任何对象。然而,如果用户在数据库中创建并拥有对象,这些对象就是与数据库用户同名的模式(schema)的一部分。模式可拥有数据库中任何类型的对象:表、索引、序列和视图等。模式拥有者或DBA可授权其他数据库用户访问这些对象。用户总是拥有完整的权限,而且可以控制用户模式中的对象。

当DBA(或其他任何拥有CREATE USER系统权限的用户)创建用户时,可将其他许多特征赋给用户,例如用户可使用哪些表空间创建对象,以及密码是否提前到期等。

可使用3种方法验证数据库中的用户的身份:数据库身份验证、操作系统身份验证和网络身份验证。使用数据库身份验证时,用户的加密密码存储在数据库中。与之相反,操作系统身份验证进行如下假设:操作系统连接已经身份验证过的用户具有和某些用户相同的权限,这些用户具有相同的或类似的名称(取决于OS_AUTHENT_PREFIX初始参数的值)。网络身份验证使用基于公共密钥基础设施(Public Key Infrastructure,PKI)的解决方案。这些网络身份验证方法需要具有Oracle高级安全性选项的Oracle 11g或12c企业版。

1.3.6 配置文件

数据库资源不是无限的,因此DBA必须为所有数据库用户管理和分配资源。数据库资源的一些示例是CPU时间、并发会话、逻辑读和连接时间。

数据库配置文件是可以赋给用户的限定资源的命名集。安装Oracle后,DEFAULT配置文件已经存在,并且系统将其赋给任何还没有显式分配配置文件的用户。DBA可添加新的配置文件或改变DEFAULT配置文件,从而符合企业的需求。DEFAULT配置文件的初始值允许无限使用所有的数据库资源。

1.3.7 序列

Oracle序列用于分配有序数,并且保证其唯一性(除非重新创建或重新设置序列)。它在多用户环境中生成一系列唯一数字,而且不会产生磁盘锁定或任何特殊I/O调用的系统开销,这一点不同于将序列加载到共享池中所涉及的情况。

序列(sequence)可生成长达38位的数字,数字序列可按升序或降序排列,间隔可以是用户指定的任何值,并且Oracle可在内存中缓存序列中的数字块,从而获得更快的性能。

序列中的数字可保证唯一,但不一定有序。如果缓存数字块,并且重新启动实例,或者回滚使用序列中数字的事务,则下次调用从序列中检索数字不会与原序列中已引用但未使用的数字相同。

1.3.8 同义词

Oracle同义词(synonym)只是数据库对象的别名,用于简化对数据库对象的引用,并且隐藏数据库对象源的细节。同义词可以赋给表、视图、物化视图、序列、过程、函数和程序包。与视图类似,除了数据字典中的定义外,同义词不会在数据库中分配任何空间。

同义词可以是公有或私有的。私有同义词在用户的模式中定义,并且只有该用户可用。公有同义词通常由DBA创建,并且所有的数据库用户都可以自动使用公有同义词。


提示:

创建公有同义词后,要确保同义词的用户拥有对该同义词引用的对象的正确权限。


引用数据库对象时,Oracle首先检查该对象是否存在于用户的模式中。如果不存在这样的对象,Oracle就检查私有同义词。如果没有私有同义词,Oracle就检查公有同义词。如果没有公有同义词,Oracle就返回错误。

1.3.9 PL/SQL

Oracle PL/SQL是Oracle对SQL的过程化语言扩展。当标准的DML和SELECT语句因为缺少过程化元素而无法以简单方式生成所需要的结果时,PL/SQL非常有用,这些过程化元素在传统的第三代语言(如C++和Ada)中很常见。从Oracle 9i开始,SQL处理引擎在SQL和PL/SQL之间共享,这意味着所有添加到SQL的新特性也可以自动用于PL/SQL。

下面简要介绍使用Oracle PL/SQL的优点。

1.过程/函数

PL/SQL过程和函数是PL/SQL命名块的范例。PL/SQL块是PL/SQL语句序列,可将其视为用于执行功能的单位,它最多包含3个部分:变量声明部分、执行部分和异常部分。

过程和函数之间的区别在于:函数将单个值返回到调用程序,如SQL SELECT语句。相反,过程不返回值,只返回状态码。然而,过程的参数列表中可能有一个或多个变量,编程人员可设置这些变量并且将其作为结果的一部分返回。

过程和函数在数据库环境中有诸多优点。在数据字典中只需编译并存储过程一次,当多个用户调用过程时,该过程已经编译,并且只有一个副本存在于共享池中。此外,网络通信量会减少,即使没有使用PL/SQL的过程化特性也是如此。一次PL/SQL调用所使用的网络带宽远小于单独通过网络发送的SQL SELECT和INSERT语句,这还没有考虑到对通过网络发送的每条语句的重新解析。

2.程序包

PL/SQL程序包(package)将相关的函数和过程以及常见的变量和游标(cursor)组合在一起。程序包由两个部分组成:程序包规范和程序包主体。在程序包规范中,提供程序包的方法和属性,方法的实现以及任何私有方法和属性都隐藏在程序包主体中。如果使用程序包而不是单独的过程或函数,则在改变内嵌的过程或函数时,任何引用程序包规范中元素的对象都不会失效,从而避免了重新编译引用程序包的对象。

3.触发器

触发器(trigger)是一种特殊类型的PL/SQL或Java代码块,在指定事件发生时执行或触发。事件类型可以是表或视图上的DML语句、DDL语句甚至是数据库事件(如启动和关闭)。可以改进指定的触发器,使其作为审核策略的一部分在特定用户的特定事件上执行。

在分布式环境中,触发器非常有用,它们可用于模仿不同数据库中的表之间的外键关系。触发器也可用于实现那些无法使用内置的Oracle约束类型定义的复杂完整性规则。

第17章将介绍触发器如何应用于健壮的分布式环境中。

1.3.10 外部文件访问

除了外部表外,Oracle还有大量其他的方法可用于访问外部文件:

●在SQL*Plus中,访问包含要运行的其他SQL命令的外部脚本,或将SQL*Plus SPOOL命令的输出发送到操作系统的文件系统中的文件。

●在PL/SQL过程中,使用UTL_FILE内置程序包读取或写入文本信息;类似地,PL/SQL过程中的DBMS_OUTPU调用可生成文本消息和诊断,另一个应用程序可以捕获这些消息和诊断,并保存在文本文件中。

●BFILE数据类型可引用外部数据。BFILE数据类型是指向外部二进制文件的指针。在BFILE可以用于数据库之前,需要使用CREATE DIRECTORY命令创建目录别名,该命令指定包含存放BFILE目标的完整目录路径的前缀。

●DBMS_PIPE可与Oracle支持的任何3GL语言通信并交换信息,如C++、Ada、Java或COBOL。

●UTL_MAIL是Oracle 10g中新增的程序包,它允许PL/SQL应用程序发送电子邮件,而不需要知道如何使用底层的SMTP协议栈。

在使用外部文件作为数据源进行输入或输出时,大量警告将按顺序出现。在使用外部数据源之前,应该仔细考虑以下方面:

●数据库数据和外部数据可能经常不同步,这种情况发生在其中一个数据源改变但没有和另一个数据源同步时。

●确保两个数据源的备份几乎同时发生,从而确保恢复数据源时能使两个数据源保持同步,这一点很重要。

●脚本文件可能包含密码,许多组织禁止在脚本文件中使用普通文本表示任何用户账户。这种情况下,操作系统身份验证方法可能好于用户身份验证方法。

●对位于由每个DIRECTORY对象所引用的目录中的文件,应回顾其安全性。引用的操作系统文件的不严格安全性降低了数据库对象上的安全效果。

1.3.11 数据库链接和远程数据库

数据库链接允许Oracle数据库引用存储在本地数据库之外的对象。命令CREATE DATABASE LINK创建到远程数据库的路径,从而允许访问远程数据库中的对象。数据库链接打包如下内容:远程数据库的名称、连接到远程数据库的方法、用于验证远程数据库连接的用户名/密码的组合。在某些方面,数据库链接类似于数据库同义词:数据库链接可以为公有或私有,并且它提供便捷的方法来访问另一个资源集。主要区别在于,资源在数据库外部而不是同一个数据库中,因此需要更多信息来解决引用问题。另一个区别在于,同义词是对特定对象的引用,而数据库链接则是定义的路径,用于访问远程数据库中任意数量的对象。

为在分布式环境中的多个数据库之间建立链接,域中每个数据库的全局数据库名必须都不相同。因此,重要的是正确分配初始参数DB_NAME和DB_DOMAIN。

为便于使用数据库链接,可将同义词赋给数据库链接,使表访问更透明;用户并不知道同义词访问的是本地对象还是分布式数据库上的对象。对象可移动到不同的远程数据库,也可以移动到本地数据库,只要同义词名相同,就可以使对象访问对用户保持透明。

第17章将进一步介绍如何在分布式环境中利用远程数据库的数据库链接。