educoder 数据库系统概论2022 触发器的创建和使用

任务描述

本关任务:通过实验,加深对数据完整性的理解,掌握创建和使用触发器。

相关知识

触发器的定义

触发器是建立在触发事件上的。例如,对表执行 INSERT 、 UPDATE 或 DELETE 等操作时, SQL Server 就会自动执行建立在这些操作上的触发器。在触发器中,包含了一系列用于定义业务规则的 SQL 语句,用来强制用户实现这些规则,从而确保数据的完整性。

触发器和存储过程的区别

触发器与存储过程的区别是运行方式的不同,触发器不能用 EXECUTE 语句调用,而是在用户执行 SQL 语句时,自动触发执行。而存储过程需要用户、应用程序或者触发器来显示地调用并执行。

触发器的优点

  • 触发器是自动的。当对表中的数据做了任何修改之后,立即被激活;

  • 触发器可以通过数据库中的相关表,进行层叠修改;

  • 触发器可以强制限制。这些限制比用 CHECK 约束所定义的更复杂,与 CHECK 约束不同的是,触发器可以引用其他表中的列。

触发器的作用

触发器的主要作用就是,其能够实现由主键和外键所不能保证的复杂的参照完整性和数据的一致性,它能够对数据库中的相关表进行级联修改,提供比 CHECK 约束更复杂的数据完整性,并自定义错误消息。触发器的主要作用主要有以下几个方面:

  • 强制数据库间的引用完整性;

  • 级联修改数据库中所有相关的表,自动触发其它与之相关的操作;

  • 跟踪变化,撤销或回滚违法操作,防止非法修改数据;

  • 返回自定义的错误消息,约束无法返回信息,而触发器可以;

  • 触发器可以调用更多的存储过程。

触发器的分类

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

DML触发器

DML 触发器是一些附加在特定表或视图上的操作代码。当数据库服务器中,发生数据操作语言事件时,执行这些操作。 SQL Server 中的 DML 触发器有三种:

INSERT 触发器:向表中插入数据时被触发;

DELETE 触发器:从表中删除数据时被触发;

UPDATE 触发器:修改表中数据时被触发。

当遇到下列情形时,应考虑使用 DML 触发器:

  • 通过数据库中的相关表实现级联更改;

  • 防止恶意或者错误的 INSERT 、 DELETE 和 UPDATE 操作,并强制执行 CHECK 约束定义的限制更为复杂的其他限制;

  • 评估数据修改前后表的状态,并根据该差异采取措施。

DDL触发器

DDL 触发器是当服务器或者数据库中发生数据定义语言(主要是以 CREATE , DROP , ALTER 开头的语句)事件时,被激活使用。使用 DDL 触发器,可以防止对数据架构进行的某些更改,还可以防止对记录数据中的更改或事件操作。

登录触发器

登录触发器会为响应 LOGIN 事件而激发存储过程,与 SQL Server 实例建立用户会话时,将引发此事件。

登录触发器将在登录的身份验证阶段完成之后,且用户会话实际建立之前激发。因此,来自触发器内部,且通常将到达用户的所有消息(例如错误消息和来自 PRINT 语句的消息)传送到 SQL Server 错误日志。如果身份验证失败,将不激发登录触发器。

触发器的工作原理

触发器触发时:

  • 系统自动在内存中创建 deleted 表或 inserted 表;

  • 只读,不允许修改,触发器执行完成后,自动删除。

inserted表

  • 临时保存插入或更新后的记录行;

  • 可以从 inserted 表中,检查插入的数据是否满足业务需求;

  • 如果不满足,则向用户报告错误消息,并回滚插入操作。

deleted表

  • 临时保存删除或更新前的记录行;

  • 可以从 deleted 表中,检查被删除的数据是否满足业务需求;

  • 如果不满足,则向用户报告错误消息,并回滚插入操作。

    inserted 表和 deleted 表对照:

操作inserted表deleted表
insert存放新增的记录……
delete……存放被删除的记录
update存放更新后的记录存放更新前的记录

创建触发器

创建触发器语法示例如下:

 
  1. CREATE TRIGGER trigger_name
  2. ON table_name
  3. [WITH ENCRYPTION]
  4. FOR | AFTER | INSTEAD OF [DELETE, INSERT, UPDATE]
  5. AS
  6. BEGIN
  7. SQL语句
  8. END

我们以 INSERT 触发器的创建为例,讲解触发器的创建和使用。首先创建测试数据表:

 
  1. --创建学生表
  2. create table student(
  3. stu_id int identity(1,1) primary key,
  4. stu_name varchar(10),
  5. stu_gender char(2),
  6. stu_age int
  7. )

为 student 表创建 INSERT 触发器:

 
  1. --创建insert触发器
  2. create trigger trig_insert
  3. on student
  4. after insert
  5. as
  6. begin
  7. if object_id(N'student_sum',N'U') is null--判断student_sum表是否存在
  8. create table student_sum(stuCount int default(0));--创建存储学生人数的student_sum表
  9. declare @stuNumber int;
  10. select @stuNumber = count(*)from student;
  11. if not exists (select * from student_sum)--判断表中是否有记录
  12. insert into student_sum values(0);
  13. update student_sum set stuCount =@stuNumber; --把更新后总的学生数插入到student_sum表中
  14. end

测试触发器:

 
  1. --测试触发器trig_insert-->功能是向student插入数据的同时级联插入到student_sum表中,更新stuCount
  2. --因为是后触发器,所以先插入数据后,才触发触发器trig_insert;
  3. insert into student(stu_name,stu_gender,stu_age)values('吕布','男',30);
  4. select stuCount 学生总人数 from student_sum;
  5. insert into student(stu_name,stu_gender,stu_age)values('貂蝉','女',30);
  6. select stuCount 学生总人数 from student_sum;
  7. insert into student(stu_name,stu_gender,stu_age)values('曹阿瞒','男',40);
  8. select stuCount 学生总人数 from student_sum;

执行上面的语句后,结果如下图所示:

另外,因为定义学生总数表 student_sum ,是向 student 表中插入数据后,才计算的学生总数。所以,学生总数表应该禁止用户,向其中插入数据。

 
  1. --创建insert_forbidden,禁止用户向student_sum表中插入数据
  2. create trigger insert_forbidden
  3. on student_sum
  4. after insert
  5. as
  6. begin
  7. RAISERROR('禁止直接向该表中插入记录,操作被禁止',1,1)--raiserror 是用于抛出一个错误
  8. rollback transaction
  9. end

结果如下:

编程要求

我们已经为你建好了数据库与数据表,并添加了相应的数据内容。
你只需:

  • 补全右侧代码片段中 create trigger_insert_score 下的 Begin-End 区域间的代码,向 score 表建立一个插入触发器。保证向 score 表中插入的学生信息的学号,必须在 student 表中存在;

  • 补全右侧代码片段 create trigger_delete_student 下的 Begin-End 区域间的代码,向 student 表插入删除触发器,实现 student 表和 score 表的级联删除;

  • 补全右侧代码片段 create trigger_protect_grade 下的 Begin-End 区域间的代码,向 score 表建立触发器,使 grade 列不能手工修改。

表 student 如下所示,其中 birthday 是 date 类型,其余字段均为 varchar 类型。

snosnamesexbirthdaydisciplineschool
1001Tommale2019-06-01computerinformation
1002Bobmale2019-06-01softwareinformation
1003Alicefemale2019-05-02computerinformation

表 score 如下所示,字段 grade 为 float 类型,其余均为 varchar 类型。

snocnograde
100110189
100110278
100210188
100210280

测试说明

本关涉及到的测试文件是 step1.sh ,平台将运行用户补全的 step1.sql 文件,得到数据,然后执行如下操作:

  • 将得到的数据与答案比较,判断程序是否正确;

  • 如果操作正确,你将得到如下的结果:

参考代码 

USE studentdb
go

SET NOCOUNT ON 
go
--********** create trigger_insert_score  **********--
--********** Begin **********--
create trigger trigger_insert_score
on score
after insert
as
Begin
if not exists(
    select *from student 
    where sno in (select sno from inserted)
    )
    Begin
    rollback transaction
    Begin transaction
    End
    End



--********** End **********--
go
delete from score
go
insert into score values('1001','2001','89.5')
go
insert into score values('1002','2001','95')
go
insert into score values('1011','2001','88')
go
select * from score
go


--********** create trigger_delete_student  **********--
--********** Begin **********--
create trigger trigger_delete_student
on student
for delete
as
begin
delete from score where sno in(select sno from deleted)
End



--********** End **********--
go
delete from student where sno='1001'
go
select * from student
go

--********** create trigger_protect_grade **********--
--********** Begin **********--
create trigger trigger_protect_grade
on score
for update
as
begin
rollback transaction
begin transaction
end



--********** End **********--
go
delete from score where sno='1001'
go
select * from score
go
select * from student
go

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实验触发器实验报告 [实验目的] 1. 理解Oracle触发器的种类和用途 2. 掌握行级触发器的编写 [预备知识] 1. PL/SQL程序设计 [实验原理] 1. 建立触发器 CREATE [OR REPLACE] TRIGGER <触发器名> BEFORE"AFTER INSERT"DELETE"UPDATE OF <列名> ON <表名> [FOR EACH ROW] WHEN (<条件>) <PL/SQL 程序块> ON 子句中的名称识别与数据库触发器关联的数据库表 触发器事件指定了影响表的 SQL DML 语句 ( INSERT、 DELETE 或 UPDATE) AFTER 指定了触发器在处理完成后触发 BEFORE 指定了触发器在处理完成前触发 默认情况下,触发器每个表触发一次 FOR EACH ROW 选项指定触发器每行触发一次(即触发器为行级触发器) 要使触发器触发,WHEN 子句中布尔型表达式的值必须判定为 TRUE 可以将 REPLACE 添加到 CREATE 语句以自动删除和重建触发器 2. 行级触发器中引用表数据 在行级触发器中,使用伪记录来表示旧数据:old和新数据:new 引用示例::new.customer_name, :old.customer_name 3. 行级触发器中的谓词 在一个多条件触发的触发器中,使用谓词可以区分当前触发的操作的类型:inserting, updating,deleting。 示例: IF Inserting THEN 语句 ; END IF; IF Updating THEN 语句 ; END IF; IF Deleting THEN 语句 ; END IF; 4. 触发器的限制 SELECT 语句必须是 SELECT INTO 语句或内部游标声明。 行级触发器不可以对触发表进行查询,包括其调用的子过程中。 不允许 DDL 声明和事务控制语句 。 如果由触发器调用存储子过程,则存储子程序不能包括事务控制语句 。 :old 和 :new 值的类型不能是 LONG 和 LONG RAW。 [实验内容] 1. 给Customer表增加一列Savings,类型为int,来存放每个顾客的存款总额。 ALTER TABLE customer ADD (saving varchar2(30)); select * from customer; 2. 更新Customer表,使得Savings字段的值正确。 3. 在Account表上增加一个行级触发器,当对account的balance进行update和insert一个 记录时同步修改Customer的Savings字段,保证数据的一致性。 4. 对account进行update操作,记录account表和customer表的变化。 5. 去掉顾客- 存款账号表中引用account表的外键约束(如果不去掉,后面的操作无法实现。当然最 佳的方法是修改其外键约束的更新策略,但考虑到复杂性,这里使用不标准的做法, 但建议大家实际运用中不要这么做)。在顾客- 存款账号表插入一条记录,表明顾客开设了一个新的账户。 6. 将一条刚才新开账户号的存款记录插入账号表,记录account表和customer表的变化。 [实验总结] 1. 实验中遇到的问题和解决的方法。 ----------------------- "触发事件 ":old ":new " "Insert "无定义,所有字段都是N"该语句完成后插入的值 " " "ULL " " "Update "更新前该行的旧值 "更新后该行的值 " "Delete "删除前该行的值 "无定义,所有字段都是N" " " "ULL "
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值