1、开始之前本教程介绍可以在数据库中创建和使用的对象。这些对象有的用于保存原始数据,有的纯粹是为了改善性能,还有些用于帮助维护数据的完整性。本教程讨论到的对象包括表、视图、索引、触发器、限制、同义词和序列。本教程还简单地探讨了这些对象的用途和如何创建它们。在本教程的末尾,简单讨论了一个内置的工具,它能够输出帮助重新创建和复制对象的 SQL 语句。关于本系列这个免费的共包含 9 篇教程的 教程系列 的目的是为了帮助您准备 Informix Dynamic Server (IDS) 基础认证考试(555)。这个认证将考察关于 IDS 11.50 管理的入门级知识,包括基础 SQL、如何安装 IDS 1
2、1.50、如何创建数据库和数据库对象、安全性、事务隔离、备份和恢复流程,以及数据复制技术。这些教程为考试的每部分打下了坚实的基础。不过,您不能仅使用这些教程作为唯一的考试准备材料。回页首关于本教程本教程讨论的主题与考试的第 4 部分 “IDS 表、视图和索引” 对应。回页首目标完成本教程之后,您应该能够: 解释数据库表的用途,以及如何创建数据库表 解释非片段表、片段表和分区表之间的区别 描述是什么限制,以及能够在 Informix 数据库中使用的不同类型的限制 解释如何在数据库中创建限制 描述视图的概念,解释视图与表的区别 解释如何创建视图 描述索引在数据库中扮演的角色,以及如何创建它们 解释
3、什么是同义词和序列,以及如何创建它们 描述什么是数据库触发器,以及如何创建它们 Informix Dynamic Server 11.50 基础考试 555 认证准备,第 4 部分: 检查数据库对象表、限制、视图、索引、触发器、序列和同义词简介: 本教程讨论可以在数据库中创建和使用的各种对象,这是 IBM Informix Dynamic Server 之旅的一站。这些对象包括表、索引、触发器和视图等。本教程讨论这些对象的定义、使用方法和创建方法。 解释如何输出数据库中的对象的 SQL 语句(模式) 回页首先决条件本教程针对需要进一步提高自己的数据库管理员。尽管具备基础的数据库知识有所帮助,但
4、不是必要的。回页首系统需求在学习本教程的过程中,您不需要安装 IDS。不过,如果您有 IDS,将能从本教程学到更多东西。如果您还没有安装 IDS,可以下载免费的试用版(见 参考资料)。对象列表讨论了数据库之后,我们继续探讨可以在数据库中创建供用户使用的对象。 表 1 描述了本教程将要讨论的对象。尽管这还不是数据库中包含的所有对象,但是将帮助您熟悉一部分对象。表 1. 对象及其说明对象 说明表 以行和列的格式储存数据限制 限制数据值以保持数据库的完整性视图 虚拟表索引 用于从表获取数据的访问方法触发器 对数据库中发生的事件的自动响应序列 生成唯一整数的数据库对象同义词 现有表、视图或序列对象的代
5、替名称表如本 系列教程 的 Informix Dynamic Server 11.50 基础考试 555 认证准备,第 1 部分:IDS 计划和安装 所述,表类似于以行和列存储数据的电子表格。每个列描述表存储的一条数据,而每个行包含关于表的主题的特定实例的一些数据。CREATE TABLE SQL 语句用于创建表。CREATE TABLE 语句的语法包含很多选项,可能变得很复杂,因此本教程不介绍该语法,但提供一些使用该语法的不同子句的例子。CREATE TABLE 语句的最简单形式是创建一个仅包含基础列的表。清单 1. 创建一个包含基础列的表CREATE TABLE customer (SSN
6、 char(11),LName char(50),Age integer,Birthday date);这个例子创建一个包含 4 个列的名为 customer 的表:SSN、LName、Age 和 Birthday。注意列的定义,它是一个包含在一对圆括号中并以逗号分隔的列表。清单 2 中的例子添加了一个储存子句。如本系列的 Informix Dynamic Server 11.50 基础考试 555 认证准备:第 3 部分:DBMS 实例和存储对象 所述,表存储在 dbspace 中。因此该储存子句列出将要创建表的 dbspace 的名称。清单 2. 储存子句CREATE TABLE cust
7、omer (SSN char(11),LName char(50),Age integer,Birthday date)IN dbspace1;下一个例子( 清单 3)添加确定大小的属性。表中的实际数据储存在称为 Informix 页 的磁盘空间中。Informix 页与 O/S 磁盘页不同。Informix 页是 Informix 执行磁盘读写时采用的 I/O 机制。Informix 页的大小在 2K 到 16K 之间。尽管数据存储在页上,IDS 不希望一个表的各个页分散到整个磁盘中,因此 IDS 使用了一个称为区段(extent)的机制。区段是指相邻的页的集合。因此表的大小可以指定希望在一
8、个区段中包含多少个页。默认情况下,区段的大小为 8 页。IDS 允许 DBA 指定分配给表的第一个区段的大小,以及分配给表的其他区段的大小。当表的第一个区段已满时,将给它分配第二个区段。当表的第一个和第二个区段已满时,将给它分配第三个区段。IDS 根据表数据需要的页的增加相应地增加区段。注意:磁盘上的区段是以页为单位的,但是在定义了区段之后,DBA 以千字节为单位调整它们的大小。因此,了解您的系统上的 Informix 页的大小非常重要。对于大部分 UNIX 和 Linux 32 位系统,默认的页大小为 2K。对于 UNIX 和 Linux 64 位系统,以及 Windows 系统,默认的页大
9、小为 4K。您还可以指定非默认的页大小。清单 3 中的例子创建了一个表,假设 Informix 页大小为 4K,它的第一个区段可以包含 50 个页,第二个区段可以包含 25 个页:清单 3. 调整大小的属性CREATE TABLE customer (SSN char(11),LName char(50),Age integer,Birthday date)EXTENT SIZE 200 NEXT SIZE 100;下一个例子( 清单 4)使用了称为 lock mode 的子句。当用户尝试更新表中的行时,必须在需要更新的区域上放置一个锁,从而使另一个用户不能在此期间更新该区域。默认情况下,表放
10、置了一个 lock mode page 锁,这表明锁定的区域为整个表。由于一个页可能包含多个行,因此即使用户仅需要更新该页的某个行,在更新期间都会锁定该页的所有行,此时其他用户不能访问该页。DBA 可以将锁定区域更改为 lock mode row。如果表放置了 lock mode row 锁,正在执行更新的用户仅锁定了他所更新的行,其他更新用户可以访问页上的其他行。 清单 4 中的例子添加了一个 lock mode row 子句;lock mode page 子句是可以替换的。清单 4. lock mode row 子句CREATE TABLE customer (SSN char(11),L
11、Name char(50),Age integer,Birthday date)LOCK MODE ROW;每种锁模式都有其优点和缺点。要详细了解锁,请阅读 Information Center 的 Administration 主题的 “Locking” 部分(见 参考资料)。片段/分区表默认情况下,表在创建之后必须完全位于一个 dbspace 中。它所在的 dbspace 可以由服务器分配给它,或在 CREATE TABLE SQL 语句中通过 IN 子句指定(见上面的 清单 2)。必须位于一个 dbspace 中给表带来了几个限制,包括大小限制。要实现更大的表,您可以创建片段表。这些特殊
12、的表打破了位于一个 dbspace 中的规则,它允许表的各个部分(片段)位于两个以上的 dbspace 中。默认情况下表是不分成片段的,需要 DBA 显式地将其划分成片段。可以通过两种方式之一将表划分成片段: 通过循环 将行均匀地分布到列出的片段中 通过表达式 指定一个将表放置到适当的片段的表达式 注意:片段表在每个 dbspace 上只能有一个片段,因此必须为表的每个片段指定一个 dbspace 名。清单 5 显示了 DBA 如何通过循环片段法创建一个新的表,从而将表分布在 3 个 dbspace 中。清单 5. 基于循环片段法创建的新表CREATE TABLE customer (SSN
13、char(11),LName char(50),Age integer,Birthday date)FRAGMENT BY ROUND ROBIN IN dbs1, dbs2, dbs3;清单 6 显示了 DBA 使用基于表达式的片段创建的新表。当每次查询表以确定需要访问哪个片段时将计算表达式。清单 6. 基于表达式的片段CREATE TABLE customer (SSN char(11),LName char(50),Age integer,Birthday date)FRAGMENT BY EXPRESSION(Age50) IN dbs1(Age 50。 由于每个 dbspace 仅能
14、包含表的一个片段,因此 CREATE TABLE SQL 语句使用 dbspace 名称来识别表片段。清单 5 和清单 6 中的例子仅是一个开始。基于循环片段的表的片段数量可以与实例中定义的 dbspace 一样多。基于表达式的片段表也可以拥有很多片段,并且可以根据需要使用更加复杂的表达式。使用更加复杂的表达式的一个例子是:清单 7. 基于更加复杂的表达式的片段FRAGMENT BY EXPRESSION (Age 51 OR Age = 17 AND LName NOT LIKE %John%) IN dbs2;尽管表达式可以变得更加复杂(甚至比清单 7 中的表达式还要复杂),但经验表明最好
15、使用比较简单的表达式,同时又要将数据很好地分布到各个片段中。选择适当的片段模式能够显著改善性能。要理解这一点,请阅读 Information Center 的 Administering 主题下的 Performance Guide 的 “Fragmentation Guidelines” 部分。分区表与片段表非常接近,唯有两个地方不同: 每个 dbspace 可以有一个或多个分区 CREATE TABLE 语法 分区表允许每个 dbspace 包含多个分区。这样做的好处是,具有 10 个分区的表可以分布在 10 以内的 dbspace 中,而具有 10 个片段的表必须使用 10 个 dbsp
16、ace。由于分区表允许每个 dbspace 上有多个分区,所以必须更改 SQL 语句的语法,因为不能再通过默认的 dbspace 名引用其中的分区。清单 8. 分区表的语法CREATE TABLE customer (SSN char(11),LName char(50),Age integer,Birthday date)PARTITION BY EXPRESSIONPARTITION part1 (Age50) IN dbs1PARTITION part2 (Age 50;注意:customer 表和 orders 表之间的实际连接在 SQL 语句访问视图时发生。取决于组成视图的 SELE
17、CT 语句的复杂程度,这看起来好像一个简单的 SELECT 语句访问视图需要很长的时间,因为实际上它在幕后运行一个更加复杂的 SELECT 语句。因为视图只不过是一种特殊类型的表,所以它能够被读取(SELECT)和修改(UPDATE、INSERT 和 DELETE)。每个语句都有其局限性。组成视图的 SELECT 语句不能使用 INTO TEMP 子句或 ORDER BY 子句。不过,访问视图的 SQL 语句能够访问这些语句。SELECT * from join_view ORDER BY OrderTotal DESC;您可以修改视图,但前提是组成视图的底层 SELECT 语句不包含: 两个
18、或多个表的连接 聚合函数或 GROUP BY 子句 UNIQUE 或 DISTINCT 关键字 UNION 关键字 计算值或字面值 其他限制适用于每个语句类型。UPDATE 语句不能更新视图的派生列。INSERT 语句也不能将派生列插入到视图中。在 INSERT 语句中,如果视图不包含表的所有底层列,那么将对这些列使用 NULL 值。通过使用 INSTEAD OF 触发器可以克服以上的一些限制。(让我们将相关讨论留在 “触发器” 小节)。VIEW SQL 语句包含 WITH CHECK OPTION 子句。当视图使用 WITH CHECK OPTION 子句时,您不能通过视图修改任何行,除非它
19、们满足视图的底层 SELECT 语句。下面是一个解释例子:CREATE VIEW cust_view_21 AS SELECT * FROM customer WHERE age = 21;上面的视图仅包含在 customer 表中其 Age 列等于 21 的行。因为在创建该视图时没有指定 WITH CHECK OPTION 子句,由于这是一个可以修改的视图,因此您可以运行以下语句插入一个新行,并且不会出现错误:INSERT INTO cust_view_21 VALUES (“123-45-6789“,“Flintstone“,“29“,“01/02/1012“);注意,插入的 Age 为
20、29,它并不等于 21。因此,如果您在前面这样做SELECT * from cust_view_21 WHERE SSN=“123-45-6789“;您将收到消息 “No rows found”。为什么?因为 Mr. Flintstone 的年龄不等于 21,因此它不在该视图中。如果您将试图修改为包含 WITH CHECK OPTION:CREATE VIEW cust_view_21 AS SELECT * FROM customer WHERE age = 21WITH CHECK OPTION;那么以上的插入语句将不能成功运行。它将收到一个 “Data value out of rang
21、e” 错误并失败。索引常规索引索引是获得最佳数据库性能的关键部分。索引是表的访问方法。当针对某个表运行 SQL 语句时,数据库服务器必须访问该表,以查找和 SQL 语句的条件匹配的行。一般情况下,数据库服务器通过两种方式来访问表:序列扫描或索引扫描。使用哪种方法取决于诸多因素,包括 SQL 语句中的选择条件(WHERE 子句中的过滤器)。选择条件告诉数据库服务器 SQL 语句是需要返回 1% 的表,还是需要返回 100% 的表。通过序列扫描访问表就行阅读一样。如果您想知道整个故事情节,您必须从第 1 页开始,然后一直阅读到最后一页。如果您从中间开始,或没有阅读完,您将丢失某些内容。因此,序列扫
22、描必须从表的第 1 页开始,然后往下读取直至表的最后一页。由于序列扫描的开销比较大,除非 SQL 语句的选择条件对此有要求,否则应该避免使用这种方法。在 OLTP 应用程序中尤其如此。OLTP 应用程序通常从很大的表获取少量页或数据。因为 OLTP 环境不能使用序列扫描,因此必须选择一种更快的访问方法。这种访问方法就是索引。再次以书为类比,书的后页都包含索引。为什么?为了更快的查阅所需的内容。如果您正在编写一个 SQL 语句,但忘记了语法,您可以通过 Guide to SQL: Syntax 开始查找 SQL 语句的语法。如果您要确保不错过查找的内容,那么必须从第 1 页开始,从此一页一页往下
23、找,直到找到所需的内容。或者您可以打开书本的索引,查找您需要的主题,然后索引就直接将该主题所在的页告诉您。哪种方法更快?如果讨论的主题就在第 3 页,序列扫描就更快;不过,如果讨论的主题在第 695 页,那么索引查找就更快。假设书的索引为 3 页。在索引查找过程中,您最多需要查看 4 页,即 3 个索引页和讨论主题所在的页。所以,只要讨论的主题不在书本的前 5 页之内,索引查找就比序列扫描快。不过要记住,序列扫描在第一次找到主题之后并不会停止,因为书本中的其他页很可能讨论相同的主题。如果序列扫描在找到主题的第一个页停止,那么它将错过其他页上的相同主题。而在索引查找中,您可以参考相同索引条目所在
24、的所有页。曾经有许多人问我,“索引如此强大,为什么不为所有东西建立索引查找?”。答案是:优势总是伴随着劣势。我们讨论了索引的优势,现在我们看看它的劣势: 磁盘空间 - 书本的索引占据一些页,表的索引也不例外 性能 - 索引延迟了对表的修改(插入、更新和删除) 由于当今磁盘空间的价格不是很昂贵,所以第一个劣势仍然在争论之中。但是任何导致性能下降的因素都要深入讨论,比如索引的第二个劣势。对表执行修改的速度下降源自额外的开销。回过头看看以书为对象的类比,如果像书本添加另一个主题,那么就需要根据新的主题更新索引。如果没有更新索引,那么需要了解新主题的人要么查找整本书,要么认为该书并未包含该主题,转而购
25、买另一本包含该主题的书。因此,当 SQL 语句更改了表(插入新行、更新现有的行或删除行)之后,必须马上更新索引,让用户能够查到到最精确的结果集。索引必须时刻完全并且精确地反映表中的所有行。如果索引没有与表保持同步,那么它就带来麻烦,并且可能导致完整性问题。因此,如果要向一个带有索引的表插入一个新行,那么您将需要做双倍的工作之后才能插入该行。您必须将行插入到数据页中,然后使用该行的信息更新索引页。添加另一个索引意味着需要做 3 倍的工作(插入一个数据页、为 index #1 插入一个索引页、为 index #2 插入一个索引页)。与向没有索引的表添加一个行相比,向带有索引的表添加第 3 个索引意
26、味着要做 4 倍的工作。更新和删除也存在相同的问题。因此,一个表的索引越多,修改数据时需要做的工作就越多,从而降低了这些操作的性能。现在,您了解了索引的功能、优势和劣势,我们接下来将学习如何为表创建索引。使用 CREATE INDEX SQL 语句创建一个新的索引:CREATE INDEX SSN_idx ON customer(SSN);默认情况下,索引允许存在重复值。就像书中的主题可以在多个页中讨论一样(所有页号都列出在索引中),表中的某个索引值也可以存在多个行中,因此索引需要指向该值存在的所有行。您可以通过指定每个索引值都必须是唯一的(在表中仅能出现一次),从而在表上放置一个唯一性限制。
27、这与在表上创建一个 UNIQUE 限制非常相似,见 “限制” 小节所述。CREATE UNIQUE INDEX SSN_idx ON customer(SSN);一个索引可以存在一个以上的列中。CREATE INDEX cust_idx ON customer (LName, Birthday);如果一个索引存在一个以上的列中,索引中列出的第一个列是索引的主要排序,然后才是其他列的排序。我们使用电话簿来解释这个想法。电话簿是根据姓排序的(主要排序),但是在电话簿中有多个关于 Jones 的列表。因此,当您查看 Joneses 页时,您可以看到电话簿进入包含 Jones 值的第二个排序,它是根据
28、名进行排序的。如果电话簿中包含多个 John Joneses,那么将使用另一个排序,即根据中间名进行排序。如果有两个 John J Joneses,那么又采用另一种排序方式,依此类推。IDS 中的多列索引也遵循这个原理。如果主排序(第一个列)级别出现重复的对象,那么这些重复的对象将根据第二个列进行排序,依此类推。索引的最大键大小(所有索引列数据类型的大小之和)是 390 字节。就像 CREATE TABLE SQL 语句一样,CREATE INDEX SQL 语句有一个储存子句。CREATE INDEX cust_idx ON customer (SSN) IN dbspace1;可以以升序或
29、降序的方式创建索引,升序是默认值。因为 IDS 能够以双向的方式遍历索引,所以指定索引的升降序并不重要。清单 24. 包含一个列索引的例子CREATE INDEX cust_idx ON customer (SSN DESC);清单 25. 包含多个列索引的例子CREATE INDEX cust_idx ON customer (birthday DESC, age);清单 25 中的例子将创建降序排列 birthday 的索引。当查找到重复的 birthday 时,将以升序的方式对 age 值进行排序,因为升序是默认值,而 age 的排序没有被指定。聚集索引聚集索引是对表进行排序的一种方式。
30、索引通常包含排序。索引的排序由 CREATE INDEX 语句中的 DESC 和 ASC 关键字决定。不过,表在默认情况下没有排序,以什么样的顺序插入行,行就以什么样的顺序储存到表的页中。当从表删除了行时,新插入的行将填充由于删除而空出来的位置。这会导致表非常混乱(没有次序)。混乱的表的劣势是出现重复搜索或范围类型搜索。例如,假设您需要获取 customer 表中姓为 Jones 的所有客户信息。如果多个客户的姓都为 Jones,您必须查找包含 Jones 的所有行,而不管它们位于哪个页上。如果姓为 Jones 的行有 10 个(非常糟糕),您最后需要读取 10 个不同的页(每页包含一个行)以
31、获取所需的数据。如果将所有相同的值集聚在一起,会发生什么情况呢?如果您在每页中包含 10 个行,那么就可以在一个页中读取包含值 Jones 的所有 10 个行。页是 I/O 的基本单位,那么哪种情况能够获得更好的性能呢?10 次 I/O 还是 1 次 I/O?由于 I/O 是最慢的操作之一,所以 I/O 越少性能就越高。所以 1 次 I/O 比 10 次 I/O 的性能高得多。这种情况也适用于范围搜索。例如,假设您需要获取根据特定时间框架排序的所有客户。您希望返回排序日期在两个日期之间的所有行。如果该表没有根据日期排序,那么要获取这两个日期之间的列,就必须搜索整个表。但是如果您根据日期对该表进
32、行排序,那么同日期的所有列将 “集聚” 在一组页中。这能提供更好的性能,因为仅需读取一组页,而不是读取整个表。表将根据索引进行聚集(排序)。要更改表的排序就必须聚集索引。通过 CREATE INDEX 语句的 CLUSTER 关键字聚集索引:CREATE CLUSTER INDEX cust_idx ON customer(SSN);注意:仅能根据一个索引以一种方式聚集(排序)表。因此,每个表只能有一个聚集索引。片段和索引还可以将索引分割成片段。当在一个片段表中创建索引时,默认情况下索引片段将和表片段的数量一样。如果表有 3 个片段,那么索引将有 3 个片段。每个索引片段仅为与它相关联的数据片
33、段的数据创建索引。还可以给索引提供一个显式的片段模式,该模式可以和表的片段模式不匹配。这仅能通过 FRAGMENT BY EXPRESSION 子句实现。FRAGMENT BY ROUND ROBIN 子句在 CREATE INDEX SQL 语句中是无效的。清单 26. FRAGMENT BY EXPRESSION 子句CREATE TABLE customer (SSN char(11),LName char(50),Age integer,Birthday date)FRAGMENT BY EXPRESSION (Age 51) IN dbs2;CREATE INDEX cust_idx
34、1 on customer(Age);CREATE INDEX cust_idx2 on customer (SSN) in dbspace5;CREATE INDEX cust_idx3 ON customer(Birthday) FRAGMENT BY EXPRESSION(Birthday 01/01/2000) in dbspace3;清单 26 中的索引在以下方面有所不同: cust_idx1 索引的片段划分方式与表的片段划分方式相同。因为表有两个片段,所以索引也有两个片段,并且它们将储存在数据所在的 dbspace 中。 cust_idx2 索引没有片段,并且整个索引将存储在 db
35、space5 中。 cust_idx3 有 3 个片段,将根据表达式将每个片段放入到适当的 dbspace 中。 就像在表上一样,索引的片段所使用的表达式可以非常复杂。不过,一般的经验是尽量保持比较简单的表达式,同时又满足性能需求。索引还可以使用 partitioned 语法在一个 dbspace 中支持多个分区。给一个片段表放置一个聚集索引将导致表的每个片段聚集起来,而不是将整个表都聚集起来。触发器触发器触发器是定义的对象,它们在数据库发生事件时生成一个自动响应。触发器的一个明显例子就是,有时通过它们保持两个表同步(复制)。因为 UPDATE 语句是一个表语句,不能通过一个 SQL 语句更新
36、两个不同的表。因此,可以在一个表上放置一个触发器,当该表的值发生改变时(事件),数据库将使用适当的信息更新另一个表(响应)。因为触发器是发生事件时的自动响应,所以在定义触发器时需要考虑一些基本注意事项: 您正在等待什么事件 您的响应是什么 什么时候根据事件发生处理响应 如何命名触发器 是否希望在响应中使用事件的值 清单 27. 示例触发器CREATE TRIGGER cust_ins_trig INSERT ON customerREFERENCING NEW AS new FOR EACH ROW WHEN (new.Birthday 01/01/2000)(EXECUTE PROCEDUR
37、E ins_mailing_list(new.LName, new.Birthday);清单 27 中的例子将获取插入到 customer 表中的客户信息,并将它的两个列(LName 和 Birthday 列)传递到需要使用该信息完成某些事情的存储过程,比如将它们插入到邮件列表表中。但是,仅当 Birthday 的值在 01/01/2000 之前,才会调用存储过程。以上 5 个注意事项的答案是:1. 您在 customer 表上等待一个 INSERT 事件 2. 您的响应是运行一个存储过程 3. 您将对每个插入的行处理响应 4. 您将触发器命名为 cust_ins_trig 5. 您使用使用
38、REFERENCING NEW AS new 子句的值。 清单 27 中的例子使用了两个可选子句,即 REFERENCING 子句和 WHEN 子句。触发的事件可以是以下之一: 在表中插入一个行 在表中删除一个行 在表中更新一个行 在表中更新一个列 在表中选择一个行 在表中选择一个列 触发器响应可以是以下之一: 将行或列插入到一个表中 从一个表删除行或列 更新表或列 执行存储过程或函数 可以在 3 个不同的时间点运行响应: 对于每个行,如清单 27 的例子所示 在触发事件处理 SQL 语句之前 在触发事件处理 SQL 语句完毕之后 清单 28. 另一个触发器例子CREATE TRIGGER c
39、ust_del_orders DELETE on customerREFERENCING OLD AS abcFOR EACH ROW( DELETE FROM orders WHERE SSN = abc.SSN);在清单 28 中的例子中,当从 customer 表删除了使用 SSN 的相关客户时,将从排序表删除所有排序。在这里,REFERENCING AS 子句改用 OLD AS 语法,因为这是需要删除的数据。您给出的引用关联值是一个变量,可以使用任意值。这个例子使用了 abc。注意:如果因为运行的 SQL 语句才发生触发事件,那么触发器响应被认为是该 SQL 语句的一部分。这意味着如果
40、该触发器响应失败,那么响应和初始的触发器事件将回滚。INSTEAD OF 触发器在 “视图” 小节讨论了通过视图给修改数据的限制。例如,您不能将一个新行插入到多表视图中。这时将用到 INSTEAD OF 触发器。INSTEAD OF 触发器是指在视图上定义的触发器。记住,触发器最重要的两个部分是触发事件和触发器响应。仅当触发器事件发生之后才会调用触发器响应。如果您创建了一个多表视图,然后试图向该视图插入数据,那么将立即返回一个错误。您可以在该视图上创建一个 INSTEAD OF 触发器,表示 “不是” 执行该事件(向视图插入数据),而是仅执行触发器的响应。清单 29. INSTEAD OF 触
41、发器例子CREATE TABLE customer ( cust_num int, Name char(50), phone char(12);CREATE TABLE orders (order_num serial, cust_num int, order_total money); CREATE VIEW join_view ASSELECT Name, order_total FROM customer, ordersWHERE customer.cust_num = orders.cust_num;CREATE TRIGGER instdof_trig INSTEAD OF INSE
42、RT ON join_viewREFERENCING NEW AS newFOR EACH ROW( INSERT INTO customer VALUES (25, new.Name, “555-1212“), INSERT INTO orders VALUES (0, 25, new.order_total);INSERT INTO join_view VALUES (“Fred“, 25.68);清单 29 中的 INSERT 语句不会收到错误,因为它实际上没有向该视图插入一个行(在多表视图中不能这样做)。相反,它发出两个 INSERT 语句。这个例子需要一些修改,因为它在某些列中包含了
43、您不希望出现的硬编码值,但是该例子帮助您了解 INSTEAD OF 触发器的用途序列序列是指用户可用于生成唯一整数的数据库对象。与存在单个表中、并且是该表的事务的一部分的 SERIAL 数据类型不同,序列对象是一个独立的对象,它可以在多个表中使用,并且独立于使用它的事务。序列使用的 INT 8 范围是从 (2 63 -1) 到 2 63-1。 使用 CREATE SEQUENCE SQL 语句创建序列:清单 30. CREATE SEQUENCE 语句CREATE SEQUENCE sequence_1 INCREMENT BY 1START WITH 0;CREATE SEQUENCE 语句
44、包含几个不同的子句,清单 30 仅显示了其中两个子句。显示的两个子句允许使用不同的值递增,以及使用标记序列开始的数字。其他一些子句能够指定最大值、最小值,以及在到达最大值之后又从最小值开始。要获得序列的当前值,需要使用 sequence.CURRVAL 表达式。要获得序列的下一个值,需要使用 sequence.NEXTVAL 表达式。可以在 SELECT、DELETE、INSERT 和 UPDATE SQL 语句中使用这些表达式。清单 31. CREATE SEQUENCE 语句的例子CREATE SEQUENCE order_num_seqINCREMENT BY 1START WITH 0
45、;INSERT INTO orders ( order_num, SSN) VALUES (order_num_seq.NEXTVAL, “111-11-1111“);SELECT order_num_seq.CURRVAL, SSN FROM orders;同义词同义词是指现有的表、视图或序列对象的代替名称。可以创建同义词来引用位于其他数据库、相同的数据库服务器或其他数据库服务器中的表或视图。 同义词允许用户编写能够引用远程表的 SQL 语句,而不需要知道远程表的语法。清单 32. 同义词例子CREATE SYNONYM orders_syns for southsalesdallas:in
46、formix.orders;CREATE SYNONYM orders_synw for westsalessanfran:informix.orders;清单 32 中的第一个同义词指向 dallas 数据库服务器上的 southsales 数据库的 orders 表。清单 32 中的第二个同义词指向 sanfran 数据库服务器上的 westsales 数据库的 orders 表。如果这两个表是数据库的本地表,那么用户就可以合并它们:SELECT * from orders_syns, orders_synw WHERE 注意:尽管同义词允许开发人员轻松地编写 SQL 代码,但它们也掩盖了
47、使用的表是远程表这一事实。远程表的访问时间更长,从而导致 SQL 语句的性能下降。对象模式索引、触发器和限制支持不同的 “模式” 操作。最基本的 3 种操作模式是: Enabled Disabled Filtering(仅在限制和唯一索引中可用) 如果一个对象被设置为 “disabled” 模式,那么数据库服务器就停止使用该对象,但不会将其从数据库中删除。使用 SET SQL 语句更改对象的模式。清单 33. 用于设置启用/禁用的 SET SQL 语句示例SET INDEXES cust_idx DISABLED;SET INDEXES cust_idx ENABLED;SET CONSTRA
48、INTS uniq_lname DISABLED;SET CONSTRAINTS uniq_lname ENABLED;可以在同一个 SQL 语句中指定同类型的一个或多个对象:SET INDEXES cust_idx, phone_idx DISABLED;当表上的索引被禁用之后,就不能再将该索引作为表的访问方法,并且对表的任何修改都不会同步到索引中。不过,该索引在数据库中仍然定义为一个对象。要重新开始使用该索引,则需要启用它。当启用索引之后,它就被完全重构,因为在索引禁用期间表的更改没有反映到索引上。当限制被设置为 disabled 之后,将不使用该限制检查定义限制的表中的数据的完整性。当重
49、新启用限制之后,将根据限制重新检查表中的所有数据的完整性,因为在禁用限制期间有一些数据可能变化了,并且可能不能满足限制条件。重新启用限制之后,如果发现一个行违反限制,那么将返回一个错误,并且不会重新启用限制。当触发器被设置为 disabled 时,在发生任何匹配事件时都不会触发触发器。当重新启用触发器之后,未来发生任何匹配事件都会触发触发器。限制和唯一索引都可以设置为 FILTERING 模式。FILTERING 模式允许使用限制或索引。但是如果修改违反限制或索引,修改的行将被写到数据库中的 violations 表中,修改语句继续保持运行。为了能够成功运行,当限制或索引被设置为 FILTERING 模式时应该为该表启动 violations 和 diagnostics 表。例子:清单 34. filtering 模式的例子CREATE TABLE customer ( Name char(50), phone char(12), age int);CREATE UNIQUE INDEX uniq_phone ON customer(pho