我看了很久,我的世界t触发器器这个组件到底怎么用

在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
标签:至少1个,最多5个
组件库中,包含有 Trigger 的组件,它们都有类似的组件结构。以下组件都可以归为这一类:
PopConfrim
DataPicker
TimePicker
TreeSelect
这些组件的特点,就是都有弹框。通过点击, hover, 或是改变焦点的方式,打开或关闭弹框。而这个特点,就是 Trigger 组件所要实现的功能:弹框可见性的控制。
先看很黄的这一组,Popover/PopConfirm -& Tooltip -& rcTooltip -& rcTrigger -& child
这类组件,可以为弹框传入自定义的内容或组件。Popover 和 PopConfirm 都是从 Tooltip 整容来的,为弹窗添加了常用的功能。
其中,内部组件 Trigger,负责为组件添加监听事件,来控制弹框的可见性。
// rc-trigger\lib\Trigger.js
var ALL_HANDLERS = ['onClick', 'onMouseDown', 'onTouchStart', 'onMouseEnter', 'onMouseLeave', 'onFocus', 'onBlur'];
可以看出,Trigger 支持 onClick, onMouseDown, onTouchStart, onMouseEnter, onFocus 来触发弹窗,通过 onClick, onMouseLeave, onBlur 来取消弹窗。弹窗的打开与关闭方式可以不是对应的,例如可以 mouseEnter 打开,click 关闭。我们写弹窗组件的时候,也能把这一部分逻辑抽象出来,感觉应该很棒。
当我们用 React 写弹窗组件的时候,肯定想过要如何添加点击取消功能。使用遮罩,会有一次无效操作,对选框类弹框就不太好了。Antd 的做法是添加全局监听事件,通过判断点击的元素,是否包含在组件或弹框中,即可判断是否为取消弹框的点击。下面是 Antd 中判断组件包含性的方法
function contains(root, n) {
var node =
while (node) {
if (node === root) {
node = node.parentN
除了添加监听事件,Trigger 还有一个很重要的功能,就是弹框的渲染。Trigger 中的弹框并没有渲染进正常的文档流中,它 mixin 了一个 getContainerRenderMixin 内部组件,该组件通过 React 的非稳定方法 unstable_renderSubtreeIntoContainer(),将组件挂载到 document.body 下。因为如果把弹窗渲染在正常文档流中,组件的定位会受到父元素定位的影响,我们知道,如果父元素为静态定位,定位标的就会变成再父一级元素。将弹窗挂载在 body 下,就能解决这个问题。
虽然弹框脱离了正常文档流,但其仍受父组件控制。而组件包裹的子组件仍然渲染在正常文档流。
// rc-util\lib\getContainerRenderMixin2
_reactDom2["default"].unstable_renderSubtreeIntoContainer(instance, component, instance._container, function callback() {
instance._component =
if (ready) {
ready.call(this);
很绿的一组,也是寄生于 Trigger,但不属于 Tooltip 一族,它们的弹窗内容被传入了定义好的组件,定制性更强。从 Trigger 这一层开始,结构都差不多。
蓝色代表忧伤,看到这么多层嵌套,看着是挺忧伤的。Popup -& Animate -& AnimateChild -& rcAlign -& PopupInner -& LazyRenderBox -& content
Popup 组件负责按参数,配置弹框的各种特性,像要不要遮罩、zIndex设多少、隐藏要不要卸载掉弹框等,把这些属性渲染到功能组件中
Animate 和 AnimateChild,顾名思义,就是搞动画的。Animate 组件类似于 React 的 ReactTransitionGroup 组件,它会调用子组件中的钩子函数,来控制动画效果。
// rc-animate\lib\AnimateChild.js
componentWillUnmount: function componentWillUnmount() {
this.stop();
componentWillEnter: function componentWillEnter(done) {
if (_util2["default"].isEnterSupported(this.props)) {
this.transition('enter', done);
componentWillAppear: function componentWillAppear(done) {
if (_util2["default"].isAppearSupported(this.props)) {
this.transition('appear', done);
componentWillLeave: function componentWillLeave(done) {
if (_util2["default"].isLeaveSupported(this.props)) {
this.transition('leave', done);
AnimateChild
AnimateChild 在生命周期的钩子函数中,通过改变类名,如改变后缀 -appear, -enter, -leave,来执行动画效果。Antd 动画主要是 CSS3 动画。
.zoom-motion(@className, @keyframeName, @duration: @animation-duration-base) {
.make-motion(@className, @keyframeName, @duration);
.@{className}-enter,
.@{className}-appear {
transform: scale(0); // need this by yiminghe
animation-timing-function: @ease-out-
.@{className}-leave {
animation-timing-function: @ease-in-out-
Align 组件负责对齐元素。前面说了,Popup 脱离了正常文档流,挂在了 document.body 上。那如何把两个风马牛不相及的元素对齐呢?还要考虑到窗口改变时也能对得上。靠的是,说的挺轻巧,反正代码我还没看懂。
// rc-align\node_modules\dom-align\lib\index.js
function domAlign(el, refNode, align) {
// masive code
PopupInner
渲染了个div,添了个 LazyRenderBox 功能组件
LazyRenderBox
这个组件很懒,因为你看不见它的时候,它就不干活了。
shouldComponentUpdate: function shouldComponentUpdate(nextProps) {
return nextProps.hiddenClassName || nextProps.
之前的一沓都是套在外面的衣服,你的 content 才是用户想看的。
尽管 React 组件树很复杂,但是最终渲染出来的 Dom 树却是格外的清爽。
Antd 不简单,看来写一套靠谱的组件可不是闹着玩的,一些看似简单的功能,实现起来却是十分的复杂。一沓代码看下来,可能就领悟了皮毛,却感觉已经受益匪浅了。
6 收藏&&|&&6
你可能感兴趣的文章
16 收藏,3.7k
10 收藏,2.2k
161 收藏,22.6k
分享到微博?
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。sqlserver 触发器实例代码
转载 &发布时间:日 00:17:33 & 作者:
何为触发器?在SQL Server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序。触发器是一个特殊的存储过程
定义: 何为触发器?在SQL Server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序。触发器是一个特殊的存储过程。 常见的触发器有三种:分别应用于Insert , Update , Delete 事件。 我为什么要使用触发器?比如,这么两个表:
代码如下: Create Table Student( --学生表 StudentID int primary key, --学号 .... ) Create Table BorrowRecord( --学生借书记录表 BorrowRecord int identity(1,1), --流水号 StudentID int , --学号 BorrowDate datetime, --借出时间 ReturnDAte Datetime, --归还时间 ... )
用到的功能有: 1.如果我更改了学生的学号,我希望他的借书记录仍然与这个学生相关(也就是同时更改借书记录表的学号); 2.如果该学生已经毕业,我希望删除他的学号的同时,也删除它的借书记录。 等等。 这时候可以用到触发器。对于1,创建一个Update触发器:
代码如下: Create Trigger truStudent On Student --在Student表中创建触发器 for Update --为什么事件触发 As --事件触发后所要做的事情 if Update(StudentID) begin Update BorrowRecord Set StudentID=i.StudentID From BorrowRecord br , Deleted d ,Inserted i --Deleted和Inserted临时表 Where br.StudentID=d.StudentID end
理解触发器里面的两个临时的表:Deleted , Inserted 。注意Deleted 与Inserted分别表示触发事件的表“旧的一条记录”和“新的一条记录”。 一个数据库系统中有两个虚拟表用于存储在表中记录改动的信息,分别是: 虚拟表Inserted 虚拟表Deleted 在表记录新增时 存放新增的记录 不存储记录 修改时 存放用来更新的新记录 存放更新前的记录 删除时 不存储记录 存放被删除的记录 一个Update 的过程可以看作为:生成新的记录到Inserted表,复制旧的记录到Deleted表,然后删除Student记录并写入新纪录。 对于2,创建一个Delete触发器
代码如下: Create trigger trdStudent On Student for Delete As Delete BorrowRecord From BorrowRecord br , Delted d Where br.StudentID=d.StudentID
从这两个例子我们可以看到了触发器的关键:A.2个临时的表;B.触发机制。 SQL触发器实例2
代码如下: /* 建立虚拟测试环境,包含:表[卷烟库存表],表[卷烟销售表]。 请大家注意跟踪这两个表的数据,体会触发器到底执行了什么业务逻辑,对数据有什么影响。 为了能更清晰的表述触发器的作用,表结构存在数据冗余,且不符合第三范式,这里特此说明。 */ USE Master GO IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'U' AND NAME = '卷烟库存表') DROP TABLE 卷烟库存表 GO IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'U' AND NAME = '卷烟销售表') DROP TABLE 卷烟销售表 GO --业务规则:销售金额 = 销售数量 * 销售单价 业务规则。 CREATE TABLE 卷烟销售表 ( 卷烟品牌 VARCHAR(40) PRIMARY KEY NOT NULL, 购货商 VARCHAR(40) NULL, 销售数量 INT NULL, 销售单价 MONEY NULL, 销售金额 MONEY NULL ) GO --业务规则:库存金额 = 库存数量 * 库存单价 业务规则。 CREATE TABLE 卷烟库存表 ( 卷烟品牌 VARCHAR(40) PRIMARY KEY NOT NULL, 库存数量 INT NULL, 库存单价 MONEY NULL, 库存金额 MONEY NULL ) GO --创建触发器,示例1 /* 创建触发器[T_INSERT_卷烟库存表],这个触发器较简单。 说明: 每当[卷烟库存表]发生 INSERT 动作,则引发该触发器。 触发器功能: 强制执行业务规则,保证插入的数据中,库存金额 = 库存数量 * 库存单价。 注意: [INSERTED]、[DELETED]为系统表,不可创建、修改、删除,但可以调用。 重要: 这两个系统表的结构同插入数据的表的结构。 */ IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'TR' AND NAME = 'T_INSERT_卷烟库存表') DROP TRIGGER T_INSERT_卷烟库存表 GO CREATE TRIGGER T_INSERT_卷烟库存表 ON 卷烟库存表 FOR INSERT AS --提交事务处理 BEGIN TRANSACTION --强制执行下列语句,保证业务规则 UPDATE 卷烟库存表 SET 库存金额 = 库存数量 * 库存单价 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 from INSERTED) COMMIT TRANSACTION GO /* 针对[卷烟库存表],插入测试数据: 注意,第一条数据(红塔山新势力)中的数据符合业务规则, 第二条数据(红塔山人为峰)中,[库存金额]空,不符合业务规则, 第三条数据(云南映像)中,[库存金额]不等于[库存数量]乘以[库存单价],不符合业务规则。 第四条数据库存数量为0。 请注意在插入数据后,检查[卷烟库存表]中的数据是否 库存金额 = 库存数量 * 库存单价。 */ INSERT INTO 卷烟库存表(卷烟品牌,库存数量,库存单价,库存金额) SELECT '红塔山新势力',100,12,1200 UNION ALL SELECT '红塔山人为峰',100,22,NULL UNION ALL SELECT '云南映像',100,60,500 UNION ALL SELECT '玉溪',0,30,0 GO --查询数据 SELECT * FROM 卷烟库存表 GO /*
结果集 RecordId 卷烟品牌 库存数量 库存单价 库存金额 -------- ------------ -------- ------- --------- 1 红塔山新势力 100 12.00 2 红塔山人为峰 100 22.00 3 云南映像 100 60.00 4 玉溪 0 30. (所影响的行数为 4 行) */ --触发器示例2
代码如下: /* 创建触发器[T_INSERT_卷烟销售表],该触发器较复杂。 说明: 每当[卷烟库存表]发生 INSERT 动作,则引发该触发器。 触发器功能: 实现业务规则。 业务规则: 如果销售的卷烟品牌不存在库存或者库存为零,则返回错误。 否则则自动减少[卷烟库存表]中对应品牌卷烟的库存数量和库存金额。 */ IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'TR' AND NAME = 'T_INSERT_卷烟销售表') DROP TRIGGER T_INSERT_卷烟销售表 GO CREATE TRIGGER T_INSERT_卷烟销售表 ON 卷烟销售表 FOR INSERT AS BEGIN TRANSACTION --检查数据的合法性:销售的卷烟是否有库存,或者库存是否大于零 IF NOT EXISTS ( SELECT 库存数量 FROM 卷烟库存表 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED) ) BEGIN --返回错误提示 RAISERROR('错误!该卷烟不存在库存,不能销售。',16,1) --回滚事务 ROLLBACK RETURN END IF EXISTS ( SELECT 库存数量 FROM 卷烟库存表 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED) AND 库存数量 &= 0 ) BEGIN --返回错误提示 RAISERROR('错误!该卷烟库存小于等于0,不能销售。',16,1) --回滚事务 ROLLBACK RETURN END --对合法的数据进行处理 --强制执行下列语句,保证业务规则 UPDATE 卷烟销售表 SET 销售金额 = 销售数量 * 销售单价 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED) DECLARE @卷烟品牌 VARCHAR(40) SET @卷烟品牌 = (SELECT 卷烟品牌 FROM INSERTED) DECLARE @销售数量 MONEY SET @销售数量 = (SELECT 销售数量 FROM INSERTED) UPDATE 卷烟库存表 SET 库存数量 = 库存数量 - @销售数量, 库存金额 = (库存数量 - @销售数量)*库存单价 WHERE 卷烟品牌 = @卷烟品牌 COMMIT TRANSACTION GO --请大家自行跟踪[卷烟库存表]和[卷烟销售表]的数据变化。 --针对[卷烟销售表],插入第一条测试数据,该数据是正常的。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '红塔山新势力','某购货商',10,12,1200 GO --针对[卷烟销售表],插入第二条测试数据,该数据 销售金额 不等于 销售单价 * 销售数量。 --触发器将自动更正数据,使 销售金额 等于 销售单价 * 销售数量。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '红塔山人为峰','某购货商',10,22,2000 GO --针对[卷烟销售表],插入第三条测试数据,该数据中的卷烟品牌在 卷烟库存表中找不到对应。 --触发器将报错。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '红河V8','某购货商',10,60,600 GO
/* 结果集 服务器: 消息 50000,级别 16,状态 1,过程 T_INSERT_卷烟销售表,行 15 错误!该卷烟不存在库存,不能销售。 */ --针对[卷烟销售表],插入第三条测试数据,该数据中的卷烟品牌在 卷烟库存表中库存为0。 --触发器将报错。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '玉溪','某购货商',10,30,300 GO /* 结果集 服务器: 消息 50000,级别 16,状态 1,过程 T_INSERT_卷烟销售表,行 29 错误!该卷烟库存小于等于0,不能销售。 */ --查询数据 SELECT * FROM 卷烟库存表 SELECT * FROM 卷烟销售表 GO /* 补充: 1、本示例主要通过一个简单的业务规则实现来进行触发器使用的说明,具体的要根据需要灵活处理; 2、关于触发器要理解并运用好 INSERTED ,DELETED 两个系统表; 3、本示例创建的触发器都是 FOR INSERT ,具体的语法可参考: Trigger语法
代码如下: CREATE TRIGGER trigger_name ON { table | view } [ WITH ENCRYPTION ] --用于加密触发器 { { { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] } [ WITH APPEND ] [ NOT FOR REPLICATION ] AS [ { IF UPDATE ( column ) [ { AND | OR } UPDATE ( column ) ] [ ...n ] | IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask ) { comparison_operator } column_bitmask [ ...n ] } ] sql_statement [ ...n ] } }
4、关于触发器,还应该注意 (1)、DELETE 触发器不能捕获 TRUNCATE TABLE 语句。 (2)、触发器中不允许以下 Transact-SQL 语句: ALTER DATABASE CREATE DATABASE DISK INIT DISK RESIZE DROP DATABASE LOAD DATABASE LOAD LOG RECONFIGURE RESTORE DATABASE RESTORE LOG (3)、触发器最多可以嵌套 32 层。 */ --修改触发器 --实质上,是将 CREATE TRIGGER ... 修改为 ALTER TRIGGER ...即可。 --删除触发器 DROP TRIGGER xxx GO --删除测试环境 DROP TABLE 卷烟库存表 GO DROP TABLE 卷烟销售表 GO DROP TRIGGER T_INSERT_卷烟库存表 GO DROP TRIGGER T_INSERT_卷烟销售表 GO ################################################################## 触发器的基础知识和例子 :create trigger tr_name on table/view {for | after | instead of } [update][,][insert][,][delete] [with encryption] as {batch | if update (col_name) [{and|or} update (col_name)] } 说明: 1 tr_name :触发器名称 2 on table/view :触发器所作用的表。一个触发器只能作用于一个表 3 for 和after :同义 4 after 与instead of :sql 2000新增项目afrer 与 instead of 的区别 After 在触发事件发生以后才被激活,只可以建立在表上 Instead of 代替了相应的触发事件而被执行,既可以建立在表上也可以建立在视图上 5 insert、update、delete:激活触发器的三种操作,可以同时执行,也可选其一 6 if update (col_name):表明所作的操作对指定列是否有影响,有影响,则激活触发器。此外,因为delete 操作只对行有影响, 所以如果使用delete操作就不能用这条语句了(虽然使用也不出错,但是不能激活触发器,没意义)。 7 触发器执行时用到的两个特殊表:deleted ,inserted deleted 和inserted 可以说是一种特殊的临时表,是在进行激活触发器时由系统自动生成的,其结构与触发器作用的表结构是一 样的,只是存放 的数据有差异。 续 下面表格说明deleted 与inserted 数据的差异 deleted 与inserted 数据的差异 Inserted 存放进行insert和update 操作后的数据 Deleted 存放进行delete 和update操作前的数据 注意:update 操作相当于先进行delete 再进行insert ,所以在进行update操作时,修改前的数据拷贝一条到deleted 表中,修改后 的数据在存到触发器作用的表的同时,也同时生成一条拷贝到insered表中 SQL Server 2000中的触发器使用 触发器是数据库应用中的重用工具,它的应用很广泛。这几天写一个化学数据统计方面的软件,需要根据采样,自动计算方差,在这里,我使用了触发器。   下面我摘录了SQL Server官方教程中的一段关于触发器的文字,确实有用的一点文字描述。   可以定义一个无论何时用INSERT语句向表中插入数据时都会执行的触发器。   当触发INSERT触发器时,新的数据行就会被插入到触发器表和inserted表中。inserted表是一个逻辑表,它包含了已经插入的数据行的一个副本。inserted表包含了INSERT语句中已记录的插入动作。inserted表还允许引用由初始化INSERT语句而产生的日志数据。触发器通过检查inserted表来确定是否执行触发器动作或如何执行它。inserted表中的行总是触发器表中一行或多行的副本。   日志记录了所有修改数据的动作(INSERT、UPDATE和DELETE语句),但在事务日志中的信息是不可读的。然而,inserted表允许你引用由INSERT语句引起的日志变化,这样就可以将插入数据与发生的变化进行比较,来验证它们或采取进一步的动作。也可以直接引用插入的数据,而不必将它们存储到变量中。   示例   在本例中,将创建一个触发器。无论何时订购产品(无论何时向Order Details表中插入一条记录),这个触发器都将更新Products表中的一列(UnitsInStock)。用原来的值减去订购的数量值即为新值。
代码如下: USE Northwind CREATE TRIGGER OrdDet_Insert ON [Order Details] FOR INSERT AS UPDATE P SET UnitsInStock = P.UnitsInStock – I.Quantity FROM Products AS P INNER JOIN Inserted AS I ON P.ProductID = I.ProductID
  DELETE触发器的工作过程   当触发DELETE触发器后,从受影响的表中删除的行将被放置到一个特殊的deleted表中。deleted表是一个逻辑表,它保留已被删除数据行的一个副本。deleted表还允许引用由初始化DELETE语句产生的日志数据。   使用DELETE触发器时,需要考虑以下的事项和原则:   ·当某行被添加到deleted表中时,它就不再存在于数据库表中;因此,deleted表和数据库表没有相同的行。   ·创建deleted表时,空间是从内存中分配的。deleted表总是被存储在高速缓存中。   ·为DELETE动作定义的触发器并不执行TRUNCATE TABLE语句,原因在于日志不记录TRUNCATE TABLE语句。   示例   在本例中,将创建一个触发器,无论何时删除一个产品类别(即从Categories表中删除一条记录),该触发器都会更新Products表中的Discontinued列。所有受影响的产品都标记为1,标示不再使用这些产品了。
代码如下: USE Northwind CREATE TRIGGER Category_Delete ON Categories FOR DELETE AS UPDATE P SET Discontinued = 1 FROM Products AS P INNER JOIN deleted AS d ON P.CategoryID = d.CategoryID
  UPDATE触发器的工作过程   可将UPDATE语句看成两步操作:即捕获数据前像(before image)的DELETE语句,和捕获数据后像(after image)的INSERT语句。当在定义有触发器的表上执行UPDATE语句时,原始行(前像)被移入到deleted表,更新行(后像)被移入到inserted表。   触发器检查deleted表和inserted表以及被更新的表,来确定是否更新了多行以及如何执行触发器动作。   可以使用IF UPDATE语句定义一个监视指定列的数据更新的触发器。这样,就可以让触发器容易的隔离出特定列的活动。当它检测到指定列已经更新时,触发器就会进一步执行适当的动作,例如发出错误信息指出该列不能更新,或者根据新的更新的列值执行一系列的动作语句。   语法 IF UPDATE (&column_name&)   例1   本例阻止用户修改Employees表中的EmployeeID列。
代码如下: USE Northwind GO CREATE TRIGGER Employee_Update ON Employees FOR UPDATE AS IF UPDATE (EmployeeID) BEGIN RAISERROR ('Transaction cannot be processed.\ ***** Employee ID number cannot be modified.', 10, 1) ROLLBACK TRANSACTION END
  INSTEAD OF触发器的工作过程   可以在表或视图上指定INSTEAD OF触发器。执行这种触发器就能够替代原始的触发动作。INSTEAD OF触发器扩展了视图更新的类型。对于每一种触发动作(INSERT、UPDATE或 DELETE),每一个表或视图只能有一个INSTEAD OF触发器。   INSTEAD OF触发器被用于更新那些没有办法通过正常方式更新的视图。例如,通常不能在一个基于连接的视图上进行DELETE操作。然而,可以编写一个INSTEAD OF DELETE触发器来实现删除。上述触发器可以访问那些如果视图是一个真正的表时已经被删除的数据行。将被删除的行存储在一个名为deleted的工作表中,就像AFTER触发器一样。相似地,在UPDATE INSTEAD OF触发器或者INSERT INSTEAD OF触发器中,你可以访问inserted表中的新行。   不能在带有WITH CHECK OPTION定义的视图中创建INSTEAD OF触发器。 示例   在本例中,创建了一个德国客户表和一个墨西哥客户表。放置在视图上的INSTEAD OF触发器将把更新操作重新定向到适当的基表上。这时发生的插入是对CustomersGer表的插入而不是对视图的插入。   创建两个包含客户数据的表:
代码如下: SELECT * INTO CustomersGer FROM Customers WHERE Customers.Country = 'Germany' SELECT * INTO CustomersMex FROM Customers WHERE Customers.Country = 'Mexico' GO
  在该数据上创建视图:
代码如下: CREATE VIEW CustomersView AS SELECT * FROM CustomersGer UNION SELECT * FROM CustomersMex GO
  创建一个在上述视图上的INSTEAD OF触发器:
代码如下: CREATE TRIGGER Customers_Update2 ON CustomersView INSTEAD OF UPDATE AS DECLARE @Country nvarchar(15) SET @Country = (SELECT Country FROM Inserted) IF @Country = 'Germany' BEGIN UPDATE CustomersGer SET CustomersGer.Phone = Inserted.Phone FROM CustomersGer JOIN Inserted ON CustomersGer.CustomerID = Inserted.CustomerID END ELSE IF @Country = 'Mexico' BEGIN UPDATE CustomersMex SET CustomersMex.Phone = Inserted.Phone FROM CustomersMex JOIN Inserted ON CustomersMex.CustomerID = Inserted.CustomerID END
  通过更新视图,测试触发器:
代码如下: UPDATE CustomersView SET Phone = ' 030-007xxxx' WHERE CustomerID = 'ALFKI' SELECT CustomerID, Phone FROM CustomersView WHERE CustomerID = 'ALFKI' SELECT CustomerID, Phone FROM CustomersGer WHERE CustomerID = 'ALFKI'
  那么具体的讲,对于多列数据,如何计算方差呢?:
代码如下: CREATE TRIGGER [calT1T2T3] ON dbo.DCLB FOR INSERT,UPDATE AS update P SET /**//* 计算方差的触发器 */ P.T1=(I.P1+I.P2+I.P3+I.P4+I.P5+I.P6), P.T2=(I.Y1+I.Y2+I.Y3+I.Y4+I.Y5+I.Y6 ), P.T3=SQRT(P.T1*P.T1+P.T2*P.T2) FROM DCLB AS P INNER JOIN Inserted AS I ON P.SID = I.SID
  触发器的使用很方便,而且也很简单,重要的是理解inserted过程。可将UPDATE语句看成两步操作:即捕获数据前像(before image)的DELETE语句,和捕获数据后像(after image)的INSERT语句。当在定义有触发器的表上执行UPDATE语句时,原始行(前像)被移入到deleted表,更新行(后像)被移入到inserted表。触发器检查deleted表和inserted表以及被更新的表,来确定是否更新了多行以及如何执行触发器动作。 认识了一下触发器的作用!
代码如下: /* 建立虚拟测试环境,包含:表[卷烟库存表],表[卷烟销售表]。 请大家注意跟踪这两个表的数据,体会触发器到底执行了什么业务逻辑,对数据有什么影响。 为了能更清晰的表述触发器的作用,表结构存在数据冗余,且不符合第三范式,这里特此说明。 */ USE Master GO IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'U' AND NAME = '卷烟库存表') DROP TABLE 卷烟库存表 GO IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'U' AND NAME = '卷烟销售表') DROP TABLE 卷烟销售表 GO --业务规则:销售金额 = 销售数量 * 销售单价 业务规则。 CREATE TABLE 卷烟销售表 ( 卷烟品牌 VARCHAR(40) PRIMARY KEY NOT NULL, 购货商 VARCHAR(40) NULL, 销售数量 INT NULL, 销售单价 MONEY NULL, 销售金额 MONEY NULL ) GO --业务规则:库存金额 = 库存数量 * 库存单价 业务规则。 CREATE TABLE 卷烟库存表 ( 卷烟品牌 VARCHAR(40) PRIMARY KEY NOT NULL, 库存数量 INT NULL, 库存单价 MONEY NULL, 库存金额 MONEY NULL ) GO --创建触发器,示例1 /* 创建触发器[T_INSERT_卷烟库存表],这个触发器较简单。 说明: 每当[卷烟库存表]发生 INSERT 动作,则引发该触发器。 触发器功能: 强制执行业务规则,保证插入的数据中,库存金额 = 库存数量 * 库存单价。 注意: [INSERTED]、[DELETED]为系统表,不可创建、修改、删除,但可以调用。 重要: 这两个系统表的结构同插入数据的表的结构。 */ IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'TR' AND NAME = 'T_INSERT_卷烟库存表') DROP TRIGGER T_INSERT_卷烟库存表 GO CREATE TRIGGER T_INSERT_卷烟库存表 ON 卷烟库存表 FOR INSERT AS --提交事务处理 BEGIN TRANSACTION --强制执行下列语句,保证业务规则 UPDATE 卷烟库存表 SET 库存金额 = 库存数量 * 库存单价 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 from INSERTED) COMMIT TRANSACTION GO /* 针对[卷烟库存表],插入测试数据: 注意,第一条数据(红塔山新势力)中的数据符合业务规则, 第二条数据(红塔山人为峰)中,[库存金额]空,不符合业务规则, 第三条数据(云南映像)中,[库存金额]不等于[库存数量]乘以[库存单价],不符合业务规则。 第四条数据库存数量为0。 请注意在插入数据后,检查[卷烟库存表]中的数据是否 库存金额 = 库存数量 * 库存单价。 */ INSERT INTO 卷烟库存表(卷烟品牌,库存数量,库存单价,库存金额) SELECT '红塔山新势力',100,12,1200 UNION ALL SELECT '红塔山人为峰',100,22,NULL UNION ALL SELECT '云南映像',100,60,500 UNION ALL SELECT '玉溪',0,30,0 GO --查询数据 SELECT * FROM 卷烟库存表 GO
/* 结果集 RecordId 卷烟品牌 库存数量 库存单价 库存金额 -------- ------------ -------- ------- --------- 1 红塔山新势力 100 12.00 2 红塔山人为峰 100 22.00 3 云南映像 100 60.00 4 玉溪 0 30. (所影响的行数为 4 行) */ --触发器示例2
代码如下: /* 创建触发器[T_INSERT_卷烟销售表],该触发器较复杂。 说明: 每当[卷烟库存表]发生 INSERT 动作,则引发该触发器。 触发器功能: 实现业务规则。 业务规则: 如果销售的卷烟品牌不存在库存或者库存为零,则返回错误。 否则则自动减少[卷烟库存表]中对应品牌卷烟的库存数量和库存金额。 */ IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'TR' AND NAME = 'T_INSERT_卷烟销售表') DROP TRIGGER T_INSERT_卷烟销售表 GO CREATE TRIGGER T_INSERT_卷烟销售表 ON 卷烟销售表 FOR INSERT AS BEGIN TRANSACTION --检查数据的合法性:销售的卷烟是否有库存,或者库存是否大于零 IF NOT EXISTS ( SELECT 库存数量 FROM 卷烟库存表 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED) ) BEGIN --返回错误提示 RAISERROR('错误!该卷烟不存在库存,不能销售。',16,1) --回滚事务 ROLLBACK RETURN END IF EXISTS ( SELECT 库存数量 FROM 卷烟库存表 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED) AND 库存数量 &= 0 ) BEGIN --返回错误提示 RAISERROR('错误!该卷烟库存小于等于0,不能销售。',16,1) --回滚事务 ROLLBACK RETURN END --对合法的数据进行处理 --强制执行下列语句,保证业务规则 UPDATE 卷烟销售表 SET 销售金额 = 销售数量 * 销售单价 WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED) DECLARE @卷烟品牌 VARCHAR(40) SET @卷烟品牌 = (SELECT 卷烟品牌 FROM INSERTED) DECLARE @销售数量 MONEY SET @销售数量 = (SELECT 销售数量 FROM INSERTED) UPDATE 卷烟库存表 SET 库存数量 = 库存数量 - @销售数量, 库存金额 = (库存数量 - @销售数量)*库存单价 WHERE 卷烟品牌 = @卷烟品牌 COMMIT TRANSACTION GO --请大家自行跟踪[卷烟库存表]和[卷烟销售表]的数据变化。 --针对[卷烟销售表],插入第一条测试数据,该数据是正常的。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '红塔山新势力','某购货商',10,12,1200 GO --针对[卷烟销售表],插入第二条测试数据,该数据 销售金额 不等于 销售单价 * 销售数量。 --触发器将自动更正数据,使 销售金额 等于 销售单价 * 销售数量。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '红塔山人为峰','某购货商',10,22,2000 GO --针对[卷烟销售表],插入第三条测试数据,该数据中的卷烟品牌在 卷烟库存表中找不到对应。 --触发器将报错。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '红河V8','某购货商',10,60,600 GO
/* 结果集 服务器: 消息 50000,级别 16,状态 1,过程 T_INSERT_卷烟销售表,行 15 错误!该卷烟不存在库存,不能销售。 */ --针对[卷烟销售表],插入第三条测试数据,该数据中的卷烟品牌在 卷烟库存表中库存为0。 --触发器将报错。 INSERT INTO 卷烟销售表(卷烟品牌,购货商,销售数量,销售单价,销售金额) SELECT '玉溪','某购货商',10,30,300 GO /* 结果集 服务器: 消息 50000,级别 16,状态 1,过程 T_INSERT_卷烟销售表,行 29 错误!该卷烟库存小于等于0,不能销售。 */ --查询数据 SELECT * FROM 卷烟库存表 SELECT * FROM 卷烟销售表 GO /* 补充: 1、本示例主要通过一个简单的业务规则实现来进行触发器使用的说明,具体的要根据需要灵活处理; 2、关于触发器要理解并运用好 INSERTED ,DELETED 两个系统表; 3、本示例创建的触发器都是 FOR INSERT ,具体的语法可参考: Trigger语法
代码如下: CREATE TRIGGER trigger_name ON { table | view } [ WITH ENCRYPTION ] --用于加密触发器 { { { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] } [ WITH APPEND ] [ NOT FOR REPLICATION ] AS [ { IF UPDATE ( column ) [ { AND | OR } UPDATE ( column ) ] [ ...n ] | IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask ) { comparison_operator } column_bitmask [ ...n ] } ] sql_statement [ ...n ] } }
4、关于触发器,还应该注意 (1)、DELETE 触发器不能捕获 TRUNCATE TABLE 语句。 (2)、触发器中不允许以下 Transact-SQL 语句: ALTER DATABASE CREATE DATABASE DISK INIT DISK RESIZE DROP DATABASE LOAD DATABASE LOAD LOG RECONFIGURE RESTORE DATABASE RESTORE LOG (3)、触发器最多可以嵌套 32 层。 */ --修改触发器 --实质上,是将 CREATE TRIGGER ... 修改为 ALTER TRIGGER ...即可。 --删除触发器 DROP TRIGGER xxx GO --删除测试环境 DROP TABLE 卷烟库存表 GO DROP TABLE 卷烟销售表 GO DROP TRIGGER T_INSERT_卷烟库存表 GO DROP TRIGGER T_INSERT_卷烟销售表 GO ################################################################## 触发器的基础知识和例子 :create trigger tr_name on table/view {for | after | instead of } [update][,][insert][,][delete] [with encryption] as {batch | if update (col_name) [{and|or} update (col_name)] } 说明: 1 tr_name :触发器名称 2 on table/view :触发器所作用的表。一个触发器只能作用于一个表 3 for 和after :同义 4 after 与instead of :sql 2000新增项目afrer 与 instead of 的区别 After 在触发事件发生以后才被激活,只可以建立在表上 Instead of 代替了相应的触发事件而被执行,既可以建立在表上也可以建立在视图上 5 insert、update、delete:激活触发器的三种操作,可以同时执行,也可选其一 6 if update (col_name):表明所作的操作对指定列是否有影响,有影响,则激活触发器。此外,因为delete 操作只对行有影响, 所以如果使用delete操作就不能用这条语句了(虽然使用也不出错,但是不能激活触发器,没意义)。 7 触发器执行时用到的两个特殊表:deleted ,inserted deleted 和inserted 可以说是一种特殊的临时表,是在进行激活触发器时由系统自动生成的,其结构与触发器作用的表结构是一 样的,只是存放 的数据有差异。 续 下面表格说明deleted 与inserted 数据的差异 deleted 与inserted 数据的差异 Inserted 存放进行insert和update 操作后的数据 Deleted 存放进行delete 和update操作前的数据 注意:update 操作相当于先进行delete 再进行insert ,所以在进行update操作时,修改前的数据拷贝一条到deleted 表中,修改后 的数据在存到触发器作用的表的同时,也同时生成一条拷贝到insered表中
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具

我要回帖

更多关于 施密特触发器的作用 的文章

 

随机推荐