LoveUnix » DB2 & Informix » DB2 基础:约束
让LU留住您的每

一天 让LU博客留住您的每一天
2004-3-3 00:05 biml2002
DB2 基础:约束<br /><br /><br />Roman B. Melnyk(roman_b_melnyk@hotmail.coml)<br />DB2 Information Development,IBM Canada Ltd.<br />2004 年 3 月<br /> <br /> <br />  <br />  内容   <br />  <br />“不能为空值!” - 非空约束 <br />  <br />“仅单独存在” - 惟一约束 <br />  <br />“头号人物!” - 主键约束 <br />  <br />“都是相关的!” - 外键约束 <br />  <br />“检查和再次检查” - 表检查约束 <br />  <br />结束语<br /> <br />  <br />参考资料 <br />约束被 DB2 Universal Database™(DB2 UDB)用来对数据实施业务规则。本文描述了下列类型的约束: <br /><br />非空(NOT NULL) <br />惟一 <br />主键 <br />外键 <br />表检查 <br />此外,还有另一种名为 信息约束(informational constraint)的约束。与上面所列的这五种约束类型不同的是,信息约束不是由数据库管理器实施的,但是 SQL 编译器可用它来提高查询性能。在这篇文章中,我将只关注上面所列的这几类约束,而不会讨论信息约束。 <br />您可以在创建一个新表时定义一个或多个 DB2 UDB 约束,也可以稍后通过更改表来定义它们。CREATE TABLE 语句是十分复杂的;所以尽管实际上其选项中只有一小部分是用于定义约束的,但是当在语法图(图 1 和 图 2)中进行查看时,那些选项本身看上去就相当复杂。通过 DB2 Control Center 可使约束管理更简单、方便。<br /><br />图 1. CREATE TABLE 语句的部分语法,显示了用于定义约束的子句<br /><br /><br />约束定义与它们所应用的数据库相关联,并存储在数据库目录中(表 1)。您可以查询数据库目录来检索并查看该信息。您可以从命令行直接进行(请记住要首先建立数据库连接),同样,您会发现通过 Control Center 来访问这些信息会更方便。<br /><br />可将所创建的约束像对待其他数据库对象一样进行处理。它们具有名称和关联模式(creator ID),并且在有些情况下还能被撤销(删除)。<br /><br />图 2. CREATE TABLE 语句的部分语法,显示了用于定义约束的子句(续)<br /><br /><br />表 1. 数据库目录中的约束信息。要运行成功,对目录的查询需要建立数据库连接。 目录视图 视图列 描述 查询实例 <br />SYSCAT.CHECKS  为每个表检查约束包含一行记录 db2 select constname, tabname, text from syscat.checks <br />SYSCAT.COLCHECKS  为表检查约束所引用的每一列包含一行记录 db2 select constname, tabname, colname, usage from syscat.colchecks <br />SYSCAT.COLUMNS NULLS 指明一列是可为空(Y)还是不可为空(N) db2 select tabname, colname, nulls from syscat.columns where tabschema = &#39;MELNYK&#39; and nulls = &#39;N&#39; <br />SYSCAT.CONSTDEP  为某些其他对象上的约束的每个依赖性包含一行记录 db2 select constname, tabname, btype, bname from syscat.constdep <br />SYSCAT.INDEXES  为每个索引包含一行记录 db2 select tabname, uniquerule, made_unique, system_required from syscat.indexes where tabschema = &#39;MELNYK&#39; <br />SYSCAT.KEYCOLUSE  为惟一、主键或外键约束定义的键中所包含的每个列包含一行记录 db2 select constname, tabname, colname, colseq from syscat.keycoluse <br />SYSCAT.REFERENCES  为每个参照约束包含一行记录 db2 select constname, tabname, refkeyname, reftabname, colcount, deleterule, updaterule from syscat.references <br />SYSCAT.TABCONST  为每个惟一(U)、主键(P)、外键(F)或表检查(K)约束包含一行记录 db2 select constname, tabname, type from syscat.tabconst <br />SYSCAT.TABLES PARENTS 该表的父表数目(该表在其中充当子表的参照约束数目) db2 &quot;select tabname, parents from syscat.tables where parents &gt; 0&quot; <br />SYSCAT.TABLES CHILDREN 该表的子表数目(该表在其中充当父表的参照约束数目) db2 &quot;select tabname, children from syscat.tables where children &gt; 0&quot; <br />SYSCAT.TABLES SELFREFS 该表的自引用参照约束数目(该表在其中既充当父表又充当子表的参照约束数目) db2 &quot;select tabname, selfrefs from syscat.tables where selfrefs &gt; 0&quot; <br />SYSCAT.TABLES KEYUNIQUE 在该表上所定义的惟一约束(除了主键)的数目 db2 &quot;select tabname, keyunique from syscat.tables where keyunique &gt; 0&quot; <br />SYSCAT.TABLES CHECKCOUNT 在该表上所定义的检查约束的数目 db2 &quot;select tabname, checkcount from syscat.tables where checkcount &gt; 0&quot; <br /><br /><br />“不能为空值!” - 非空约束<br /><br /><br />非空约束(NOT NULL constraint)防止向一列添加空值。这就确保了该列在表中每一行都存在一个有意义的值。例如,SAMPLE 数据库中 EMPLOYEE 表的定义包括 LASTNAME VARCHAR(15) NOT NULL,这就确保每行都将包含一个雇员的姓。<br /><br />要判断一列是否可为空,您可以查阅该表的数据定义语言(DDL)(可通过调用 db2look 工具来生成);您也可以使用 DB2 Control Center(图 3 和 图 4);或者您还可以查询数据库目录(清单 1)。<br /><br />图 3. DB2 Control Center 的内容窗格中显示了在其对象树中选中的关联了特定数据库的表。该列表是在 melnyk 模式上筛选的。<br /><br /><br />DB2 Control Center 让您方便地访问诸如表这样的数据库对象。图 3 显示了 SAMPLE 数据库中的用户表。当在对象树中选中 Tables 时,它们就会出现在其内容窗格中。如果选择 EMPLOYEE 表,我们可以打开 Alter Table 窗口来查看表定义,包括列属性(图 4)。<br /><br />图 4. Alter Table 窗口提供了一个方便方式来查看表属性。<br /><br /><br />清单 1. 查询数据库目录以判断哪些数据库列可为空<br /><br />db2 select tabname, colname, nulls<br />    from syscat.columns<br />    where tabschema = &#39;MELNYK&#39; and nulls = &#39;N&#39;<br /><br /> <br /><br />“仅单独存在” - 惟一约束<br /><br /><br />惟一约束(unique constraint)防止一个值在表中的特定列里出现不止一次。它还防止一组值在特定的一组列里出现不止一次。必须将惟一约束中所引用的列定义为非空(NOT NULL)。可在 CREATE TABLE 语句中使用 UNIQUE 子句(图 1 和 图 2)或者在如下的 ALTER TABLE 语句中定义惟一约束。<br /><br />清单 2. 创建惟一约束。除了 ORG_TEMP 中的 LOCATION 列不能为空且在其上定义了惟一约束之外,ORG_TEMP 表与 SAMPLE 数据库中的 ORG 表是相同的。<br /><br />db2 create table org_temp (<br />    deptnumb smallint not null,<br />    deptname varchar(14),<br />    manager smallint,<br />    division varchar(10),<br />    location varchar(13) not null)<br /><br />db2 alter table org_temp add unique (location)<br /><br />db2 insert into org_temp<br />    values (10, &#39;Head Office&#39;, 160, &#39;Corporate&#39;, &#39;New York&#39;)<br /><br />DB20000I  The SQL command completed successfully.<br /><br />db2 insert into org_temp<br />    values (15, &#39;New England&#39;, 50, &#39;Eastern&#39;, &#39;New York&#39;)<br /><br />SQL0803N  One or more values in the INSERT statement, UPDATE statement, or<br />foreign key update caused by a DELETE statement are not valid because the<br />primary key, unique constraint or unique index identified by &quot;1&quot; constrains<br />table &quot;MELNYK.ORG_TEMP&quot; from having duplicate rows for those columns.<br />SQLSTATE=23505<br /><br /> <br /><br />惟一约束通过防止无意的复制有助于确保数据的完整性。本例中,它防止插入第二条指定 New York 为该组织某部门位置的记录。惟一约束是通过惟一索引来实施的。<br /><br />约束名<br />如果您在创建约束时没有指定名称,DB2 将提供一个基于创建时间标记的名称。如:SQL031229211328410。<br /> <br /><br />“头号人物!” - 主键约束<br /><br /><br />主键约束(primary key constraint)确保了表中构成主键的一列或一组列的所有值是惟一的。主键用于识别表中的特定行。每个表只能有一个主键,但可以有几个惟一键。主键约束是惟一约束的特例,它是通过主索引来实施的。<br /><br />必须将主键约束中所引用的列定义为非空(NOT NULL)。可在 CREATE TABLE 语句中使用 PRIMARY KEY 子句(图 1 和 图 2)或者在如下的 ALTER TABLE 语句中定义主键约束。<br /><br />清单 3. 创建主键约束。EMPLOYEE 表中的 EMPNO 列不能为空,并可在其上定义主键约束。<br /><br />db2 alter table employee add primary key (empno)<br /><br /> <br /><br />您也可以使用 DB2 Control Center 来定义表上的主键约束(图 5)。<br /><br />图 5. Alter Table 窗口提供了一个方便方式来定义表上的主键约束。从 available columns 的列表中选择一个或多个列并单击按钮以将选中的列名移至 primary key columns 列表中。选中的列必须不可为空。<br /><br /><br />“都是相关的!” - 外键约束<br /><br /><br />外键约束(foreign key constraint)有时候称作参照约束。参照完整性(referential integrity)被定义为“数据库的所有外键值都是有效的状态”。那外键又是什么呢?外键(foreign key)是指表中的一列或一组列,其值必须至少匹配其父表中一行的一个主键或惟一键值。这真正意味着什么呢?它实际上并非如听起来那般复杂。简单来说,它意味着如果表(T2)中的一列(C2)存在值匹配另一个表(T1)中的一列(C1)的值,并且 C1 是 T1 的主键,那么 C2 就是 T2 中的外键列。将包含了父键(主键或惟一键)的表称为 父表(parent table),而将包含了外键的表称为 子表(dependent table)。让我们来考虑一个实例。<br /><br />SAMPLE 数据库中的 PROJECT 表有一个称为 RESPEMP 的列。该列中的值表示负责该表中所列的每个项目的雇员编号。RESPEMP 是不能为空值的。因为该列对应了 EMPLOYEE 表中的 EMPNO 列,并且我们知道 EMPNO 是 EMPLOYEE 表的主键,RESPEMP 就可以定义为 PROJECT 表中的外键(清单 4)。这将确保今后对 EMPLOYEE 表进行的删除不会让 PROJECT 表包含“不存在的”项目负责雇员。<br /><br />可在 CREATE TABLE 语句中使用 FOREIGN KEY 子句(图 1 和图 2)或者在如下的 ALTER TABLE 语句中定义外键约束。<br /><br />清单 4. 创建外键约束。<br /><br />db2 alter table project add foreign key (respemp) references employee on delete cascade<br /><br /> <br /><br />REFERENCES 子句指向此参照约束的父表。定义外键约束的语法包括 规则从句(rule-clause),在其中您可以从参照完整性角度告诉 DB2 如何处理 update 或 delete 操作(图 1)。<br /><br />将以标准方式处理 Insert 操作,您不能对其进行控制。参照约束的 插入规则(insert rule)是指外键的插入值必须匹配其父表中的某个父键值。这是有道理的,并且与上述内容一致。如果向 PROJECT 表插入一条新记录,那么该记录必须包含对 EMPLOYEE 表中一个现有记录的引用(通过父-外键关系)。<br /><br />参照约束的 更新规则(update rule)是指 外键(foreign key)的更新值必须匹配其父表中的某个父键值,并且当完成父键(parent key)上的 update 操作时,所有的外键值必须有匹配的父键值。总的来说,这意味着不能存在任何“孤儿”;每个子表必须有一个父表。<br /><br />参照约束的 删除规则(delete rule)是当从父表中删除一行时应用的,并且依赖于在定义参照约束时所指定的选项。如果指定了 RESTRICT 或 NO ACTION 子句,就不能删除任何一行。如果指定了 SET NULL 子句,则会将每个可为空的外键列设置为 null。然而,如果在创建参照约束时指定了 CASCADE 选项,那么 delete 操作将会被传播到父表的各子表上。因为已指定这些子表与父表是 删除关联的(delete-connected)。<br /><br />下列实例说明了这些观点。<br /><br />清单 5. 演示了外键约束中的更新规则和删除规则。<br /><br />db2 update employee set empno = &#39;350&#39; where empno = &#39;000190&#39;<br />DB20000I  The SQL command completed successfully.<br /><br />db2 update employee set empno = &#39;360&#39; where empno = &#39;000150&#39;<br />SQL0531N  The parent key in a parent row of relationship<br />&quot;MELNYK.PROJECT.SQL040103212526610&quot; cannot be updated.  SQLSTATE=23504<br /><br />db2 &quot;select respemp from project where respemp &lt; &#39;000050&#39; order by respemp&quot;<br /><br />RESPEMP<br />-------<br />000010<br />000010<br />000020<br />000030<br />000030<br /><br />db2 delete from employee where empno = &#39;000010&#39;<br />DB20000I  The SQL command completed successfully.<br /><br />db2 &quot;select respemp from project where respemp &lt; &#39;000050&#39; order by respemp&quot;<br /><br />RESPEMP<br />-------<br />000020<br />000030<br />000030<br /><br /> <br /><br />父表(EMPLOYEE)中为“000190”的 EMPNO 值 可以 被更改,因为子表(PROJECT)中不存在为“000190”的 RESPEMP 值。然而,对于为“000150”的 EMPNO 值就不是这样的了,它在 PROJECT 表中有匹配的外键值,因而不能被更新。指定了 CASCADE 选项的删除规则确保了当从 EMPLOYEE 表中删除主键值时,删除关联的 PROJECT 表将丢失包含相匹配的外键值的所有记录行。<br /><br />“检查和再次检查” - 表检查约束<br /><br /><br />表检查约束(table check constraint)对将要添加到表中的数据实施已定义的限制。例如,一个表检查约束可确保每当在 EMPLOYEE 表中添加或更新电话分机时,雇员的电话分机号码都正好为四位数字。可在 CREATE TABLE 语句中使用 CHECK 子句(图 1 和 图 2)或者在如下的 ALTER TABLE 语句中定义表检查约束。<br /><br />清单 6. 创建表检查约束。PHONENO_LENGTH 约束确保向 EMPLOYEE 表添加的电话分机正好为四位数字。<br /><br />db2 alter table employee add constraint phoneno_length check (length(rtrim(phoneno)) = 4)<br /><br /> <br /><br />您也可以使用 DB2 Control Center 来定义表检查约束(图 6)。<br /><br />图 6. Alter Table 窗口提供了一个方便方式来定义一列上的表检查约束。<br /><br /><br />单击 Add 按钮以定义新约束(将打开 Add Check Constraint 窗口),或者单击 Change 按钮以修改在列表中选中的现有的约束(图 7)。<br /><br />图 7. Change Check Constraint 窗口让您修改现有的检查条件。<br /><br /><br />如果表中的现有行包含违反新约束的值,您就不能创建此表检查约束(图 8)。在适当更新了那些不兼容的值之后,您就可以成功添加或修改此约束了。<br /><br />图 8. 如果新的表检查约束与表中现有的值不兼容,则会返回一条错误。<br /><br /><br />延迟数据检查<br />SET INTEGRITY 语句可用于将表置于检查暂挂状态。这就允许进行定义新的检查约束的 ALTER TABLE 语句的执行,而不需检查该表中的现有数据。<br /> <br /><br />使用 SET INTEGRITY 语句可以打开或者关闭表检查约束。这将非常有用,例如,当在给表加载大型数据的期间优化性能时。清单 7 呈现了一个简单场景,展示了使用 SET INTEGRITY 语句的一种可能方式。本例中,将雇员“000100”的电话分机更新为 123,然后关闭 EMPLOYEE 表的完整性检查。在 EMPLOYEE 表上定义要求电话分机值为 4 位数字的检查约束。创建名为 EMPL_EXCEPT 的异常表;这个新表的定义是 EMPLOYEE 表的镜像。然后打开完整性检查,而违反检查约束的行将被写入异常表中。对这些表的查询将证实有问题的行现在仅存在于异常表中。<br /><br />清单 7. 使用 SET INTEGRITY 语句来延迟约束的检查。<br /><br />db2 update employee set phoneno = &#39;123&#39; where empno = &#39;000100&#39;<br /><br />db2 set integrity for employee off<br /><br />db2 alter table employee add constraint phoneno_length check (length(rtrim(phoneno)) = 4)<br /><br />db2 create table empl_except like employee<br /><br />db2 set integrity for employee immediate checked for exception in employee use empl_except<br />SQL3602W  Check data processing found constraint violations and moved them to<br />exception tables.  SQLSTATE=01603<br /><br />db2 select empno, lastname, workdept, phoneno from empl_except<br /><br />EMPNO  LASTNAME        WORKDEPT PHONENO<br />------ --------------- -------- -------<br />000100 SPENSER         E21      123<br /><br />  1 record(s) selected.<br /><br /> <br /><br />结束语

页: [1]
查看完整版本: DB2 基础:约束


Powered by Discuz! Archiver 5.5.0  © 2001-2006 Comsenz Inc.