探析SQLServer触发器与完整性约束的区别
2016-11-02李虎军金泉邢旺张政
李虎军 金泉 邢旺 张政
摘要: 触发器与完整性约束功能强大、使用灵活,该文着重对在不同情况下两者使用时的优缺点进行了探析,以期能对数据库应用系统设计者与管理者进行数据管理、提高数据完整性控制提供有益帮助。
关键词:完整性约束;触发器;区别
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)23-0010-03
如何保证数据库应用系统中数据的正确性、一致性,是数据库应用系统设计者与管理者必须要考虑的重要内容。通常,关系数据库管理系统通过数据完整性规则(也称数据约束),来避免数据库中存在错误数据。除了数据完整性规则外,关系数据库管理系统还可以通过触发器来实现更复杂的数据约束和业务逻辑规则。尽管触发器几乎可以代替数据完整性来实现数据的正确性和一致性检查,但触发器和数据完整性各有优缺点,在使用时数据库应用系统设计者应充分考虑两者的区别。
1 数据约束
数据约束通常分为实体完整性、值域完整性、参照完整性和用户定义的完整性。
1.1 四类完整性约束
实体完整性规定在数据表中不允许有主键完全相同的两条记录存在以及主键不允许出现NULL值,也被称为主键约束。每一张数据表都应建立主键约束。
参照完整性是指一个数据表A(参照表)中某列的数据必须已经在本表某个指定列中或同一数据库中某个指定表B(即被参照表)中存在;当参照表A中有记录参照被参照表B中数据时,禁止删除被参照表B中相关数据,或修改被参照表B中相关数据时同时级联修改参照表A中相应数据,也称为外键约束(即FOREIGN KEY 约束)。通过在参照数据与被参照数据之间建立关联关系,可以很好地维护表之间数据一致性。
值域完整性限制向表中输入的值的范围。例如,在表中属性为tinyint的列只能输入0到255范围内的整数值。
用户定义的完整性,就是针对某一具体关系数据库的约束条件,它反映某一具体应用所涉及的数据必须满足的语义要求。例如我国职工年龄在必须满18周岁。用户自定义的完整性可以通过多种方式得到体现。有非空约束、CHECK约束、唯一性(UNIQUE)约束、默认值约束等。
1.2 各种约束的特点
主键约束,确保了数据表中主码不会出现重复,也就保证了表中不会出现重复的记录。在定义主键约束时如果没有指定 CLUSTERED 或 NONCLUSTERED,并且也没有为 UNIQUE 约束指定聚集索引,则将对该主键约束默认使用 CLUSTERED,即意味着数据表将按主键进行物理排序并建立聚簇索引。每一张数据表只能有一个主键约束,主键约束可以在数据表的列级或表级上进行定义,但只能二选一。
外键约束,将两个表中的数据关联起来,当其中关联数据进行插入、删除、更新时按约束建立时设定的规则进行处理,保证关联字段的数据一致性。被参照数据在被参照表中为主键。可以设定参照数据与被参照数据之间的级联操作规则,具体规则可以分为拒绝操作、级联操作、置NULL值、置默认值。
非空约束,指定某一列不允许存在空值或NULL(NULL代表值未知或未定义),即要求指定列必须包含数据。此约束可以避免指定列的值出现未知状态,要求相应列必须包含数据,有助于维护数据的完整性。
CHECK约束,对表输入数据时按设定的条件进行检查,只有符合条件的数据才允许进入数据表。可以在单列或多列上建立CHECK约束,一个表上可定义多个CHECK约束。CHECK约束同外键约束的相同之处在于,都是通过检查数据的值合理性来实现数据完整性的维护。CHECK约束中不能包含子查询,但指定条件必须是逻辑表达式,不可能很复杂。CHECK约束处理速度快,功能相对简单。CHECK约束检查的数据只能在同一张表上,不能引用其他表的数据。在执行INSERT或UPDATE语句时CHECK约束将验证数据。通过限制列可接受的值,CHECK 约束进一步实现值域完整性。
UNIQUE约束通常用于非主键的一列或多列上不输入重复的值,要求各记录该列值必须互不相同。UNIQUE约束允许该列上存在NULL值(但也只能有一个),而主键约束却不允许出现NULL值。可以在一个表上定义多个UNIQUE约束,却只能设置一个主键约束。当建立UNIQUE约束后,SQL Server会自动在该约束控制的列上创建UNIQUE索引以强制执行数据的唯一性约束。当有重复数据输入表中时,SQL SERVER会自动给出错误信息,并拒绝执行命令。
默认值约束指在插入一条记录时如果某字段(在设计表时指定默认值)没有给出具体值,则系统自动赋予其默认值。
2 触发器
通常,数据库系统开发人员采用上述数据约束实现数据完整性。但面对复杂的检查策略和操作时,SQL Server还提供了一种对数据进行检查和操作的方法——触发器。
2.1 触发器概念
触发器是用户定义在数据表上的一种被事件驱动的由DBMS调用执行的特殊存储过程。在数据库对象或在数据表上定义了相应的触发器后,当用户在数据表中修改、删除、插入记录或创建数据库对象或执行特定操作时将自动执行相应的触发器。
2.2 触发器的特点
触发器不能接收参数,也不能被用户直接调用执行。触发器以及引起触发器被执行的相应操作被统一当做一次事务处理。如果该事务未能成功执行,则DBMS会自动回滚到该事务执行前的状态。
根据触发事件类别不同,触发器可以分为DML(Data Manipulation Language)触发器和DDL(Data Definition Language)触发器。
DDL触发器用于响应各种DDL事件。DDL事件主要对应于CREATE、DROP、ALTER语句和用户登录与退出等操作。DDL触发器可被用于应用系统管理任务,还可用于审核与规范数据库中对表结构、视图结构上的操作;当数据库或表结构发生变化时激发触发器,可用触发器记录相应的修改过程,还可限制用户对数据库修改,禁止删除指定表等操作,从而实现对数据库系统的安全管理。
DML触发器是在用户使用DML语句对数据进行(删除、更新、插入)操作时触发执行。DML事件针对表或视图的INSERT、DELETE和UPDATE语句。在表或视图上处理数据时可通过DML触发器来进行相关业务规则检查,进一步提高数据完整性。
根据在触发语句前后执行的不同,触发器可分为INSTEAD OF触发器和AFTER(FOR)触发器两种。
INSTEAD OF触发器(可定义在数据表、视图上)是在数据变动之前被调用执行,并替代变动数据的操作语句,转而执行触发器所定义的操作。INSTEAD OF触发器可以替代用户的相关操作语句,在用户操作执行前进行触发器规定的相关操作。
AFTER触发器(不能定义在视图上)是在数据操作完成以后被调用执行,因此可利用AFTER触发器对变动后的数据进行相关业务规则检查,可以接受或拒绝数据的改变。
不能在临时表或系统表上创建触发器。
2.3 触发器代码要简短高效
触发器是与激发它的SQL语句属于同一事务的一部分,此SQL语句要等到触发器执行结束时才算真正完成。如果在触发器的语句体中编写了需要运行较长时间的代码,也就意味着激发触发器的每一段代码都运行较长时间,如此将会使得应用系统执行速度降低。在触发器执行中如果检测到错误,则整个事务也自动回滚,所以触发器的代码执行时间长将会使整个应用系统执行效率降低。
3 合理选择触发器与完整性约束
数据约完整性束和触发器的作用机制不同,在完成不同任务时各有优势与不足。
3.1 首选完整性约束
通常,能用完整性约束满足应用系统业务逻辑需要的,就用完整性约束。
建立表的主键约束、外键约束、值域约束和用户定义的完整性约束,对一个关系数据库系统来说是基本要求,各种关系数据库管理系统对上述的几种完整性都有很好的支持。实体完整性是在数据库管理系统的最低级别上通过索引进行强制,这些索引或是 PRIMARY KEY 和 UNIQUE 约束的一部分,或是在约束之外独立创建的。参照完整性则应尽可能使用FOREIGN KEY 约束进行强制。如果域完整性可以满足应用程序的需求,应通过 CHECK 约束进行强制。使用完整性约束进行应用系统业务逻辑检查,DBMS处理速度快,效率高。
3.2 使用触发器以满足复杂的特殊需求
触发器的主要优点在于可以使用 SQL 代码处理复杂逻辑。因此,触发器可以支持完整性约束的所有功能;但它在功能上并不总是最好的方法。在完整性约束所支持的功能无法满足应用程序的功能要求时,触发器就极为有用。例如:除非在定义外键时建立了级联引用操作,否则外键约束只能通过与另一列中的值完全匹配的值来验证列值。
触发器可通过数据库中的相关表实现级联更改;不过,通过级联参照完整性约束可以更有效地执行这些更改。触发器可以禁止或回滚违反参照完整性的更改,从而取消所尝试的数据修改;当更改外键且新值与主键不匹配时,此类触发器就可能发生作用。
1)CHECK约束满足不了特殊检查需要
CHECK约束快且效率高,但不能完成所有检查工作。触发器可以定制检查规则,并且这些定制的检查规则可比CHECK约束所定义的规则更复杂、更强大。CHECK 约束只能作用于同一表中的数据,只能根据逻辑表达式或同一表中的另一列来验证列值。如果应用程序要求根据另一个表中的列验证列值,则需要使用触发器。触发器可以引用其他表中的列。例如,触发器可以使用同一数据库中另一个表中的 SELECT结果比较插入或更新的数据。
2)对被更新的中间数据进行处理
当我们对表中某字段的过去或现在的值不感兴趣,只希望知道变化值是多少时,虽然没有列或表提供这些变化信息,但可以利用触发器中的inserted临时表和deleted临时表对相应记录值进行相减计算就可得到是否发生变化信息。触发器也可以评估数据修改前后的表状态,并根据其差异采取对策。一个表中的多个同类触发器(INSERT、UPDATE 或 DELETE)允许采取多个不同的对策以响应同一个修改语句。
3)在数据库设计时方便完整性更改
当表结构发生改变时,在原表上所建立的各种完整性约束都需要更改,有些需要先删除后才能更改表结构;而建立表后,还需重新建立各种约束,较难保证没有遗漏或差错,这就给数据库设计者带来很大不便。触发器不关心表结构的改变,其只关心是否能正确运行。因此对于使用触发器建立的约束,在表结构发生更改时只需禁用触发器即可,可为数据库设计者提供很大的便利。在数据库开发中可以先使用触发器完成引用完整性,在开发完毕的时候将它改为DRI(声明引用完整性)。
4)方便数据库大批量数据导入
在数据库开发完毕或维护时,当需要大批量导入数据时,此时可能希望关闭完整性方便导入数据;在这种情况下,如果使用触发器实现数据完整性,就能更好地体现出触发器的优点来。通过关闭触发器以减少导入时的过量开销并实现数据的快速导入处理。
5)实现不同数据库、不同服务器之间数据同步
参照完整性约束建立的数据一致性,不能跨数据库实例。当需要在不同数据库实例中建立数据一致性时(或数据备份、同步时),可以使用触发器来实现。触发器还可用于在不同服务器之间进行数据库同步。对同构数据库进行同步,可以将源数据库更新了的内容生成相应临时表,传输到目标数据库,调用相应存储过程进行同步。对于异构数据库(不同DBMS),可采用XML文件作为数据交换的中介,在相应应用系统支持下进行同步。异构数据库之间进行数据交换和同步的主要时机在于数据变化时的捕获,通过触发器可以较方便地得到实现。
6)对数据库用户进行特殊审计
当数据库用户进行一些特殊操作时,通过触发器进行记录,方便数据库管理员进行安全检查。例如,在密码表中指定列上设定AFTER(FOR)触发器,当相应的用户名或密码发生改动时,将操作者、操作对象、操作时间、前后变化了的数据等内容进行保存并通知数据库管理员,为及时发现黑客、保障数据库的安全提供帮助。还可以使用触发器实现对数据表中特定数据的禁止修改或权限检查。例如,很多注册系统在用户注册后都不能更改用户名,但该功能很多是由应用程序决定的, 如果通过打开数据库表进行更改,就可以更改用户名。为避免该情况发生,可以在触发器中利用回滚或使语句出错的方法来实现禁止更改用户名功能,使对特定数据的更改操作无效。
7)使用触发器传递自定义信息
完整性约束只能通过标准的系统错误信息传递错误信息。如果应用程序要求使用自定义信息或较为复杂的错误处理,则可以在触发器中使用raiserror( )函数来完成。
3.3 注意触发器与完整性约束的执行顺序
如果触发器表上存在完整性约束,完整性约束则在 INSTEAD OF触发器执行后但在 AFTER触发器执行前检查这些约束条件。如果完整性约束被破坏,则回滚INSTEAD OF触发器操作并且不执行 AFTER触发器。
3.4 精简设计触发器
触发器可以禁止或回滚违反业务逻辑规则的操作,但在触发器中应尽量少用回滚。回滚将会撤销已完成的大量操作,将给系统运行带来很大开销,特别是影响大批量数据时,很可能造成数据库系统服务性能的急剧下降。完整性约束是在实际操作发生之前执行的,在所有工作完成前阻止失败的事情发生。也就意味着约束的运行速度要快一些。触发器中代码应该短小精悍,如果触发器中语句复杂或影响数据量大,使用触发器所带来的可能负面效率影响可能很大。因此建议尽可能使用完整性约束,少使用触发器。
4 结束语
触发器功能强大,灵活使用触发器可以帮助数据库系统设计者和维护者实现许多复杂的功能,能较好地提高信息系统的数据完整性。但要慎用触发器,如果滥用会造成数据库系统的维护困难,应合理选择使用完整性约束和触发器。
参考文献:
[1] 王珊.数据库系统概论[M].北京:高等教育出版社,2012.
[2] 邹建.深入浅出SQL Server 2005开发、管理与应用实例[M]. 北京:人民邮电出版社,2008.
[3] 李霞. SQL Server约束在维护数据完整性中的运用[J].晋城职业技术学院学报,2012(5).