1.触发器

①含义:

  触发器为特殊类型的存储过程,可在执行SQL语句事件时自动生效。

②分类:

  SQL Server 包括三种常规类型的触发器:DML 触发器、DDL 触发器和登录触发器。

③创建语法:

IF OBJECT_ID (N'触发器名称', N'TR') IS NOT NULL
    DROP TRIGGER 触发器名称
GO
create trigger 触发器名称
on 对应的操作表
for INSERT、UPDATE 或 DELETE
as
    T-SQL
go

④常用触发器操作:

SQL server 触发器弹出异常提示 sql server触发器代码_表名

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_02

-- 1.查询当前数据库下的触发器
select * from sysobjects where xtype='TR'
go

--2.删除触发器:drop trigger --多个已逗号隔开
drop trigger 触发器名称
go

-- 3.重命名触发器名称
exec sp_rename 原名称, 新名称
go

-- 4.禁用触发器:alter table 表名 disable trigger 触发器名称
alter table 表名 disable trigger all --禁用所有触发器
alter table 表名 disable trigger 触发器名称 --禁用指定触发器,多个以逗号相隔

-- 5.启用:alter table 表名 enable trigger 触发器名称  
alter table 表名 enable trigger all --禁用所有触发器
alter table 表名 enable trigger 触发器名称 --禁用指定触发器,多个以逗号相隔

-- 6.查看触发器内容
exec sp_helptext '触发器名称'

View Code

2.DML触发器

类别

简写

含义

AFTER(FOR)

for

用触发器里面的SQL语句替换需要执行的操作,例如Delete

INSTEAD OF


待SQL执行完后才触发触发器中的T-SQL语句

 

 

 

 

3.触发器操作:insert、update、delete

-- CREATE TABLE

SQL server 触发器弹出异常提示 sql server触发器代码_表名

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_02

IF OBJECT_ID (N'Product') IS NOT NULL
    DROP TABLE Product
GO
-- 创建物品信息
CREATE TABLE Product(
productId INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
productName VARCHAR(30) NOT NULL,
productCordNo VARCHAR(50) NOT NULL,
productPrice decimal(10,2) NOT NULL,
createtime_product datetime NOT NULL default(getdate()),
last_updatetime datetime NOT NULL
)
GO

-- 创建收费单,关联物品信息ID
IF OBJECT_ID(N'chargeorder') IS NOT NULL
    DROP TABLE chargeorder
GO
CREATE TABLE chargeorder (
chargeorderId INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
chargeorderPrice decimal(10,2) NOT NULL, --实收物品售价
Number INT CHECK(Number>0), -- 支付数量
Amount as(chargeorderPrice*Number) persisted, --计算列:实收金额
paytime datetime NOT NULL DEFAULT(GETDATE()),-- 收费单支付时间
createtime_chargeorder datetime NOT NULL DEFAULT(GETDATE()), --创建时间
last_updatetime_chargeorder datetime ,--最后更新时间
FK_ProductId INT FOREIGN KEY REFERENCES Product(productId) --创建外键约束
)
GO

IF OBJECT_ID(N'chargeorderdeteli') IS NOT NULL
    DROP TABLE chargeorderdeteli
GO
-- 创建收费单详情,关联收费单ID
CREATE TABLE chargeorderdeteli(
chargeorderdeteliId INT NOT NULL  IDENTITY(1,1) PRIMARY KEY,
chargeorderdeteliName VARCHAR(50) NOT NULL, --对应商品名称
chargeorderdeteliPyType INT NOT NULL DEFAULT('0'), --对应支付类型
chargeorderdeteliPySee INT NOT NULL, -- 账单支付人
createtime_chargeorderdeteli DATETIME NOT NULL DEFAULT(GETDATE()) ,-- 创建时间
last_updatetime_chargeorderdeteli datetime NOT NULL, --最后更新时间
FK_chargeorderId INT FOREIGN KEY REFERENCES chargeorder(chargeorderId) -- 创建外键约束chargeorderId
)
GO

View Code

-- INSERT SQL

SQL server 触发器弹出异常提示 sql server触发器代码_表名

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_02

-- Product
INSERT INTO Product(productName,productCordNo,productPrice,last_updatetime) VALUES
('健齿白牙膏','20192200898E45',12.00,GETDATE()),
('健齿白牙膏-优惠款','20192200898E50',10.00,GETDATE()),
('黑人牙膏-A款','20192200898E46',24.00,GETDATE()),
('黑人牙膏-B款','20192200898E47',36.00,GETDATE()),
('高露洁牙膏-A款','20192200898E48',18.00,GETDATE()),
('高露洁牙膏-B款','20192200898E49',20.00,GETDATE())
GO
-- chargeorder
INSERT INTO chargeorder(chargeorderPrice,Number,paytime,last_updatetime_chargeorder,FK_ProductId) VALUES
(12.00,3,GETDATE(),GETDATE(),1),
(24.00,3,GETDATE(),GETDATE(),3),
(18.00,3,GETDATE(),GETDATE(),5)
GO
-- chargeorderdeteli
INSERT INTO chargeorderdeteli(chargeorderdeteliName,chargeorderdeteliPyType,chargeorderdeteliPySee,last_updatetime_chargeorderdeteli,FK_chargeorderId) VALUES
('健齿白牙膏',1,219,GETDATE(),1),
('黑人牙膏-A款',3,4990,GETDATE(),2),
('高露洁牙膏-A款',3,4936,GETDATE(),3)
GO

View Code

①触发器-insert

-- 触发器新增:对Product表插入的物品价格进行insert时候触发,价格不能为负数,否则触发器触发回滚,插入失败

-- 触发器新增:对Product表插入的物品价格进行insert时候触发,价格不能为负数,否则触发器触发回滚,插入失败
IF OBJECT_ID(N'tg_insert',N'TR') IS NOT NULL
    DROP TRIGGER tg_insert
GO
CREATE TRIGGER tg_insert
ON Product
FOR INSERT
AS
BEGIN
    DECLARE @Price decimal(10,2)
    SELECT @Price= Product.productPrice FROM Product INNER JOIN inserted ON Product.productPrice=inserted.productPrice
    PRINT @Price
    IF(@Price<0.00)
        BEGIN
            RAISERROR('插入的商品价格必须大于0.00',16,8)
            ROLLBACK TRAN
        END
END
GO

--执行测试SQL:价格为负数

INSERT INTO Product(productName,productCordNo,productPrice,last_updatetime) VALUES
('测试数据1','20192200898E45',-1,GETDATE())
GO

--测试结果:

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_07

②触发器-update

-- 触发器更新:更新chargeorder的chargeorderId的同时,更新chargeorderdeteli表FK_chargeorderId,这里属性设定为自增了,所以无法进行修改,其他update操作同理
IF OBJECT_ID(N'tg_update',N'TR') IS NOT NULL
    DROP TRIGGER tg_update
GO
CREATE TRIGGER tg_update
ON chargeorder
FOR UPDATE
AS
BEGIN
    IF UPDATE(chargeorderId)
    UPDATE chargeorderdeteli SET chargeorderdeteli.FK_chargeorderId=inserted.chargeorderId FROM chargeorder,inserted,deleted WHERE chargeorder.chargeorderId=deleted.chargeorderId
    IF @@ERROR>0
    BEGIN
        RAISERROR('操作失败',16,8)
        ROLLBACK TRAN
    END
END
GO

③触发器-delete

 -- 修改原始delete操作,改为update

-- 将原始delete操作修改为update操作
IF OBJECT_ID(N'tg_deleteupdate',N'TR') IS NOT NULL
    DROP TRIGGER tg_deleteupdate
GO
CREATE TRIGGER tg_deleteupdate
ON Product
INSTEAD OF DELETE
AS
BEGIN
    DECLARE @Product_Id int
    set @Product_Id=(SELECT Product.productId FROM Product INNER JOIN deleted ON Product.productId=deleted.productId)
    UPDATE Product SET productName='被修改的数据' WHERE Product.productId=@Product_Id
    IF @@ERROR>0
    BEGIN
        RAISERROR('操作失败',16,8)
        ROLLBACK TRAN
    END
END
GO

-- 原数据形式

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_08

   -- 执行delete后结果

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_09

 -- 执行delete操作往Log_delete日志中添加被删除的记录

SQL server 触发器弹出异常提示 sql server触发器代码_表名

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_02

IF EXISTS(select * from sysobjects where name='Log_delete')
drop table Log_delete
go
create table Log_delete(
logContent varchar(255),
last_updatetime datetime
)
go

View Code

-- 执行删除操作delete触发触发器,向Log_delete表中添加被删除的记录信息
IF OBJECT_ID(N'tg_delete',N'TR') IS NOT NULL
    DROP TRIGGER tg_delete
GO
CREATE TRIGGER tg_delete
ON Product
FOR DELETE
AS
BEGIN
    DECLARE @Product_Id VARCHAR(255),@TIME DATETIME
    SELECT @Product_Id=productId FROM deleted SET @TIME=GETDATE()
    INSERT INTO Log_delete(logContent,last_updatetime) VALUES('{被删除的物品Id为:'+@Product_Id+'}',@TIME)
    IF @@ERROR>0
    BEGIN
        RAISERROR('!!!!',16,8)
        ROLLBACK TRAN
    END
END
GO

-- 测试数据:测试将当前表的前面个tg_deleteupdate触发器禁用

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_12

-- 测试结果

SQL server 触发器弹出异常提示 sql server触发器代码_表名_13

4.问题

①多条insert语句一起执行,触发器失效?不做价格控制

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_14

 ②delete插入到log表的时候,删除一个不存在的ID插入空数据?

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_15

 

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_16

 ③使用IN来包含,只记录最前数据

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_17

 

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_18

5.对应第4点解决方案

①在insert或者delete的时候,多条并行数据插入(INSERT INTO Product(productName,productCordNo,productPrice,last_updatetime) VALUES('测试数据001','20192200898E49',20,GETDATE()),('测试数据002','20192200898E49',20.00,GETDATE())GO)或者delete(DELETE FROM Product WHERE productId IN(25,26)GO)的方式运行,触发触发器将这部分变动内容写入到log时候只成功第一条返回结果?

-- 处理方法:使用游标

SQL server 触发器弹出异常提示 sql server触发器代码_表名

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_02

-- 执行删除操作delete触发触发器,向Log_delete表中添加被删除的记录信息,支持含IN作为集合条件
IF OBJECT_ID(N'tg_delete',N'TR') IS NOT NULL
    DROP TRIGGER tg_delete
GO
CREATE TRIGGER tg_delete
ON Product
FOR DELETE 
AS
BEGIN
    DECLARE @Product_Id VARCHAR(255),@TIME DATETIME
    DECLARE CUR_DELETE CURSOR FOR SELECT deleted.productId FROM deleted  /*声明游标并指明游标操作的对象*/
    SELECT @Product_Id=productId FROM deleted
    SET @TIME=GETDATE()
    OPEN CUR_DELETE -- 打开游标
    FETCH NEXT FROM CUR_DELETE INTO @Product_Id --读取下一行
    WHILE @@FETCH_STATUS=0
    BEGIN
    PRINT '@@Start'
    INSERT INTO Log_delete(logContent,last_updatetime) VALUES('{被删除的物品Id为:'+@Product_Id+'}',@TIME)
    PRINT '@@NEXT PgDn'
    FETCH NEXT FROM CUR_DELETE INTO @Product_Id -- 读取下一行
        IF @@ERROR>0
        BEGIN
            RAISERROR('过程错误',16,8)
            ROLLBACK TRAN
        END
    END
    CLOSE CUR_DELETE -- 关闭游标
    DEALLOCATE CUR_DELETE
    PRINT '@@successfully'
END
GO

View Code

-- 测试数据:

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_21

-- 测试结果:

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_22

-- 日志中添加其他字段信息

SQL server 触发器弹出异常提示 sql server触发器代码_表名

SQL server 触发器弹出异常提示 sql server触发器代码_SQL_02

-- 执行删除操作delete触发触发器,向Log_delete表中添加被删除的记录信息,支持含IN作为集合条件
IF OBJECT_ID(N'tg_delete',N'TR') IS NOT NULL
    DROP TRIGGER tg_delete
GO
CREATE TRIGGER tg_delete
ON Product
FOR DELETE 
AS
BEGIN
    DECLARE @Product_Id VARCHAR(255),@TIME DATETIME,@ProductName VARCHAR(50)
    DECLARE CUR_DELETE CURSOR FOR SELECT productId,productName FROM deleted  /*声明游标并指明游标操作的对象*/
    --SELECT @Product_Id=productId,@ProductName=productName FROM deleted
    SET @TIME=GETDATE()
    OPEN CUR_DELETE -- 打开游标
    FETCH NEXT FROM CUR_DELETE INTO @Product_Id,@ProductName --读取下一行
    WHILE @@FETCH_STATUS=0
    BEGIN
    PRINT '@@Start'
    INSERT INTO Log_delete(logContent,last_updatetime) VALUES('{被删除的物品Id为:'+@Product_Id+','+@ProductName+'}',@TIME)
    PRINT '@@NEXT PgDn'
    FETCH NEXT FROM CUR_DELETE INTO @Product_Id,@ProductName -- 读取下一行
        IF @@ERROR>0
        BEGIN
            RAISERROR('过程错误',16,8)
            ROLLBACK TRAN
        END
    END
    CLOSE CUR_DELETE -- 关闭游标
    DEALLOCATE CUR_DELETE
    PRINT '@@successfully'
END
GO

View Code

SQL server 触发器弹出异常提示 sql server触发器代码_触发器_25