对于设计和建立数据库彻底是个新手?不要紧,Joe Celko, 世界上读者数量最多的SQL做者之一,会告诉你这些基础。和往常同样,即便是最专业的数据库老手,也会给他们带来惊喜。Joe是DMBS杂志是多年来最受 读者喜好的做者。他在美国、英国,北欧,南美及非洲传授SQL知识。他在ANSI / ISO SQL标准委员会工做了10年,为SQL-89和SQL-92标准作出了杰出贡献。算法
在第1篇到第4篇,咱们建立了表,架构的基础和可视化。第5篇和第6篇讲了存储过程。这篇文章会讲述在触发器上,你须要尽可能避免的特性。sql
一般你会被告知,触发器是存储过程的“特殊类别”,但这不彻底对。它没有参数,你不能触发它,它有其它地方不存在本地伪表(local pseudo-tables)。在咱们详细讨论前,咱们停下来,讨论下在基础表里,修改数据的SQL模型。数据库
SQL是面向集合语言,所以它用数据集合同时修改表。它不是面向记录语言,它会一次修改一条记录来处理数据,按顺序来(想下磁带和打孔卡)。修改表内容的3个基本操做是INSERT,UPDATE和DELETE。安全
插入是它们最容易理解的。这是概念上的模型,不是实际的实现。你把行集合放入现存表。但比这更多。这个集合一次做为整个单元,所以用CURRENT_TIMESTAMP这样常量的任何函数调用,对于全部的新行有一样的值。标识(IDENTITY)表性质(性质,不是列),当在增长表级别计数器的时候,经过查询行和尝试写入违反了这个模型。IDENTITY值不肯定性,取决于在插入时索引和 机械装置的物理状态。
服务器
在ANSI/ISO标准和T-SQL的INSERT里,新行保存在伪表里,命名为新。若是插入会把表引入违反任何约束的状态,那么事务会被系统回滚,你会收到错误信息。那就是说新行毫不会插入到表(可是,标识属性会增长)。网络
删除是相似的。在ANSI/ISO标准和T-SQL里DELETE里,在基表里符合DELETE FROM语句的WHERE字句的全部行,放入伪表里,命名为旧。若是删除会把表引入违法任何约束的状态,那么事务会被系统回滚,你会收到错误信息。那就是说旧行毫不离开表。架构
实际上,大多数SQL引擎会在一次经过里标记旧行,在最后经过里检查约束并移除它们。函数
更新是删除和插入的组合。概念模型是咱们在WHERE子句上查找并建立删除伪表,就像一个DELETE FROM。而后咱们看SET子句上建立新的要插入的伪表。工具
表由行组成,行由列组成。在SET子句里每一个赋值同时完成。若是一列从SET子句里丢失,SQL引擎实际上建立一个“SET <列名>=<列名>"--什么也不作的赋值。这不像过程化语言从左到右处理。这个语句会在列a和列b里交换值,且一次完成。 sqlserver
UPDATE Foobar SET a = b, b = a;
这个过程语句会把列a和列b设置为一样的值,由于它从左到右执行。
BEGIN SET a = b; SET b = a; END;
删除行会被移除,插入行在表里持续,全部都一次完成。若是更新会把表引入违反任何约束的状态,那么事务会被系统回滚,你会收到错误信息。那就是说表毫不修改。
触发器是附加到一个且只有一个表的代码。但一个表能够有多个触发器。有CREATE TRIGGER语句,由于它是持续架构对象。基本的T-SQL语法很是直接:
CREATE TRIGGER <trigger name> ON <table or view name> {FOR | AFTER | INSTEAD OF} {[INSERT] [,] [UPDATE] [,] [DELETE]} AS <trigger body>;
trigger name和table name或view name就是对应的对象名称。AFTER和FOR是等价的关键字,但AFTER更有描述行(其它SQL会BEFORE触发器)。稍后咱们会讲解INSTEAD OF选项。
INSERT,UPDATE,DELETE被称为数据库事件或动做。SELECT语句,DROP和ALTER不是数据库事件。当在表上这些操做中的一个完成,在数据库动做成功完成后,触发器会被触发。对于每一个数据库事件,触发器主体里的代码在表层级上一次执行。
注意。你的程序更新表T1,它触发触发器TR1,它更新表T1自己。由于表T1被更新,触发器TR1再次触发,如此循坏。可能毫无休止。但它不须要在那里中止。你的触发器能够引发在其它表上触发器触发。触发器也能够内嵌的。假设程序更新表T1,触发了触发器TR1,它更新了表T2。在T2上的触发器触发,再次更新表T1。这个模式能够扩展到多个表,不中止循环。
SQL引擎须要跟踪全部这些修改,这样的话若是有东西出错的话,它能够进行回滚。避免写这样的代码:它耗资源,运行缓慢且会锁掉整个数据库。它也很难维护。
触发器主体是一块T-SQL过程化语句块。但有一点区别。你不能穿参数给触发器主体,像一个存储过程。触发器主体会访问INSERTED和DELETED伪表。若是你想要的话,能够重命名这些伪表。
这个格式也有特殊的逻辑功能:UPDATE(<列名>)和COLUMNS_UPDATED()。这些测试来看一个UPDATE FROM或INSERT INTO语句在他们的参数列表里是否修改一个或多个列。这是专有功能,所以接下来我会详细讲解,一个简答的例子会是:
CREATE TRIGGER No_Embezzlement_Trigger ON Payroll AFTER UPDATE AS IF UPDATE(payroll_amt) BEGIN RAISEERROR ('You cannot give yourself a raise'); ROLLBACK TRANSACTION; END;
更新视图被熟知为一个NP彻底问题。在英语里,那是说,咱们知道在合理的次数里没有常规的方式来完成它,就如问题变得愈来愈大。
INSTEAD OF触发器是咱们更新视图并回避RDBMS的算法限制。一个NSTEAD OF触发器执行触发器主体,不是它触发的数据库动做。这用例子来解释更容易。假设咱们有以在两个表上有join和聚合函数定义的视图:
CREATE VIEW SalesSummary (order_nbr, order_amt_tot) AS SELECT O.order_nbr, O.customer_name, SUM(D.unit_price * D.order_qty) FROM Orders AS O, Order_Details AS D WHERE O.order_nbr = D.order_nbr GROUP BY O.order_nbr;
若是对SalesSummary数据库事件有个触发器,不使用INSTEAD OF触发器的话它会失败。这个视图有join,计算和聚合来确保它是不可更新的。但INSTEAD OF触发器不是一个前触发器!真正的前触发器会执行它的代码,而后尝试完成数据库事件。INSTEAD OF触发器会独自执行它的代码并完成。
记住视图是虚拟的:它物理且持续不存在。那就是说你没有已删除和已插入的伪表来使用。再进一步,你不能从INSERT INTO,DELETE FROM或UPDATE语句里拿到参数。全部代码须要在基础表上操做。
INSTEAD OF触发器也能够和视图同样运行在基础表上。一般它不这样用。T-SQL习惯上进行AFTER触发器,检查下结果和修正,或者若是有问题的话回滚。
使用触发器的一个常见技巧是在一个或多个表里收集审计数据。一般认为这不是个好主意。它经过增长额外的读写下降了应用的性能。对于SELECT语句没有触发器,所以你不能跟踪谁在查看数据。健康保险携带和责任法案(HIPAA (Health Insurance Portability and Accountability Act))和许多其它法律须要对每一个查看的数据都有记录。
但不止这些,审计的基本原则是审计从被审计的事物分离。例如,当一个采购订单建立了一个货运,负责运输的人和赞成这份订单的不是同一我的。发运到你本人的诱惑避免这个方法。
假设表上有列用户事件日期和雇员更新记录的用户id,这由触发器来完成。直到你考虑到它,这才听起来不错。触发器一直在,不会被推翻。哎呀!若是你删除了行,审计数据也一块儿删掉。若是有人能够访问审计列,它们会被更新为任何值。
第三方审计工具使用事务日志文件,它是服务器为恢复和用来捕获全部须要审计的动做的网络带宽建立,来保持数据安全,物理上从剩下的数据库分离。这会经过法律测试。记住ROI在今天的美国里,表示”监禁的危险(Risk of Incarceration)“,不是”投资回报率(return on investment)“。
若是你有旧的SQL代码迁移到新发布的SQL,你能够看下是否有触发器能够用DRI(引用完整性)约束和动做来替换。这是触发器的初衷。例如,在订单表里一个订单被删除,触发器会删除订单明细表里的全部相关记录。能够问下前辈,当咱们忘记触发器或用错它们,花了多少时间来找无关联的行。
如今,这些完整性触发器的大部分能够用声明DRI操做来代替。它们对DELETE和UPDATE动做进行简单的动做。这个动做是在DDL上的选项子句。完整语法是:
FOREIGN KEY (<referencing table column list>) REFERENCES <referenced table name> (<referenced table column list>) [ON UPDATE | ON DELETE][NO ACTION | CASCADE | SET NULL | SET DEFAULT]
NO ACTION:一个错误信息告诉用户这个操做不容许,咱们获得一个回滚。
CASCADE:在外键关系里删除或更新全部涉及到行数据。
SET NULL:设置引用列为NULL。这假设对于表,全部外键列容许接受NULL。
SET DEFAULT:这是引用表列为定义的默认值。这假设对于表,全部列有定义的默认值。
只有在完整性规则复杂的时候才使用触发器。一个我能想到的,当一个成员插入或删除时,在多个组涉及值的重发布的例子。即便那样,考虑把它放入存储过程。
T-SQL对DDL触发器也有扩展。这些被DDL事件触发,而不是DML事件——CREATE, ALTER, DROP, GRANT, DENY, REVOKE 或 UPDATE STATISTICS语句。
对于同个数据库事件有不止一个触发器是合法的。这不是个好主意,但在T-SQL里是合法的。尝试在一个触发器里完成,这样的话容易维护。
默认状况下,在SQL Server表里,对于同个操做的多个触发器是不肯定的。可是,对于两个AFTER触发器使用系统存储过程settriggerorder声明触发顺序仍是可能的。这个存储过程不能用在INSTEAD OF触发器。
语法很是直接:
EXEC sp_settriggerorder @triggername = <explains itself>, @order = [FIRST|LAST|NONE], -- firing order @stmttype = [INSERT|UPDATE|DELETE], --trigger type @namespace = [DATABASE|SERVER|NULL] ; -- explains itself
参数@order表示触发起是否第一个仍是最有一个触发。若是指定NONE,那么没有强制顺序,咱们回到了默认的状态。显然你不能有2个FIRST或2个LAST触发器。
可是,若是你有第3个触发器,它不是FIRST,也不是LAST,那它确定在触发顺序里的中间。
各位,不止T-SQL,有触发器的专有实现。所以它们不迁移。它们行为的一些能够是非肯定性的,所以它们很难调试。它们不告诉优化器它可使用的任何东西,像DRI动做作的。但是你颇有可能会发现,它们是确认复杂数据完整行规则的最安全方法。偶第神啊!
http://www.sqlservercentral.com/articles/Stairway+Series/71822/