Bootstrap

【数据库系统概论】第5章 数据库完整性【!触发器】

目录

5.1数据库完整性概述

5.2 实体完整性

5.3 参照完整性

5.4 用户定义的完整性

属性上的约束

1. 列值非空(NOT NULL)

2. 列值唯一(UNIQUE)

3. 检查列值是否满足条件(CHECK)

元组上的约束

5.5 完整性约束命名子旬

*5.6 域的完整性限制(不需要上,故这没有内容)

5.7 触发器

5.7.1 定义与激活触发器

SQL SERVER (T-SQL)语法格式

after类 型触发器(for)

inserted表和deleted表

instead of类型触发器

5.7.2 修改与查看触发器

使用sp_rename命令重命名触发器

查看触发器

 禁用或启用触发器

变 量

5.7.3 删除触发器


5.1数据库完整性概述

数据库的完整性定义

数据的正确性

数据是符合现实世界语义,反映了当前的实际状况

数据的相容性

数据库同一对象在不同关系表中的数据是相同的

例如: 学生所选的课程必须是学校开设的课程学生所在的院系必须是学校已成立的院系等

数据的完整性 ·

防止数据库中存在不符合语义的数据,防止数据库中存在不正确的数据

防范对象:不合语义的、不正确的数据

数据的安全性

保护数据库防止恶意的破坏和非法的存取

防范对象:非法用户和非法操作

5.2 实体完整性

关系模型的实体完整性

CREATE TABLE中用PRIMARY KEY定义

单属性构成的码有两种说明方法

定义为级约束条件

CREATE TABLE Student (
    StudentID INT PRIMARY KEY,   -- 定义StudentID为主键
    Name VARCHAR(100),
    Age INT
);

定义为级约束条件

CREATE TABLE Student (
    StudentID INT,
    Name VARCHAR(100),
    Age INT,
    CONSTRAINT pk_StudentID PRIMARY KEY (StudentID)   -- 定义StudentID为主键
);

多个属性构成的码只有一种说明方法-----定义为级约束条件

插入或对主码列进行更新操作时,关系DBMS按照实体完整性规则自动进行检查。

检查主码值是否唯一,如果不唯一则拒绝插入或修改

检查主码的各个属性是否为空,只要有一个为空就拒绝插入或修改

检查记录中主码值是否唯一的方法是进行全表扫描

全表扫描十分耗时

关系数据库管理系统一般都在主码上自动建立一个索引

5.3 参照完整性

在 CREATE TABLE中用FOREIGN KEY短语定义哪些列为外码
REFERENCES短语指明这些外码参照哪些表的主码

SC表中增加一个元组

SC表的Sno字段是外键,引用的是Student表中的主键Sno。如果你在SC表插入一个元组,并且这个Sno的值在Student表中找不到匹配的记录,就会违反外键约束,导致参照完整性破坏。

修改SC表中的一个元组

原本SC表中的Sno属性值在Student表中存在相应的记录,但你修改了SC表中的Sno属性,使得新的值在Student表中找不到匹配的记录,同样会破坏参照完整性。

从Student表中删除一个元组

Student表中删除了一条记录,而SC表中仍然存在引用这条记录的外键(Sno),那么SC表中这些记录的外键值就会变得无效,导致参照完整性被破坏。

修改Student表中一个元组的Sno属性

Student表中修改了某个元组的主键(Sno),而SC表中存在引用该主键的外键(Sno),则SC表中的外键值将失效,导致参照完整性破坏

(1) 拒绝执行 不允许该操作执行。该策略一般设置为默认策略

(2) 级联操作 当删除或修改被参照表(Student)的一个元组导致与参照表(SC)的不一致,则删除或修改参照表中的所有导致不一致的元组

(3)设置为空值 当删除或修改被参照表的一个元组时造成了不一致,则将参照表中的所有造成不一致的元组的对应属性设置为空值。


5.4 用户定义的完整性

针对某一具体应用的数据必须满足的语义要求

属性上的约束

属性上的约束(也叫列级约束)是应用于单个列的数据限制

1. 列值非空(NOT NULL)

NOT NULL 约束用于确保某个属性的值不能为 NULL。当列定义了 NOT NULL 约束时,必须为该列提供有效值,不能留空。

CREATE TABLE Student (
    StudentID INT NOT NULL,   -- StudentID不能为NULL
    Name VARCHAR(100) NOT NULL,  -- Name不能为NULL
    Age INT
);

这个例子中,StudentIDName 字段都被约束为 NOT NULL,意味着它们不能存储空值。

2. 列值唯一(UNIQUE)

UNIQUE 约束用于确保某个列的每个值都是唯一的,即该列中的所有值都不能重复。

CREATE TABLE Student (
    StudentID INT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    Email VARCHAR(100) UNIQUE,  -- Email列的值必须唯一
    Age INT
);

在这个例子中,Email 字段定义了 UNIQUE 约束,确保每个学生的电子邮件地址是唯一的。

3. 检查列值是否满足条件(CHECK)

CHECK 约束用于检查列的值是否满足指定的条件表达式。它可以用来限制列值的范围、格式或其他条件。

CREATE TABLE Student (
    StudentID INT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    Age INT CHECK (Age >= 18),  -- 确保Age大于或等于18
    GPA DECIMAL CHECK (GPA BETWEEN 0 AND 4)  -- 确保GPA在0到4之间
);

在这个例子中,Age 列的值必须大于或等于18,GPA 列的值必须在0到4之间。

元组上的约束

元组上的约束(也叫表级约束)是对整个元组(行)进行约束,通常是对多列之间的关系进行限制。元组级的约束可以涉及多个属性,并且可以定义不同属性之间的相互约束。

CHECK 短语用于元组级约束【插入/修改】

CHECK 约束不仅可以用于单列,还可以用于多个列之间的逻辑约束,进行更复杂的验证。例如,限制两列之间的值关系,或者根据某些列的组合来制定条件。


5.5 完整性约束命名子旬

完整性约束的命名会使用 CONSTRAINT 子句,并且可以给每个约束指定一个名字

CONSTRAINT <完整性约束名> <完整性约束>
  • <完整性约束名> 是你为约束定义的名称,用于标识约束。

  • <完整性约束> 是实际的约束类型,比如 NOT NULLUNIQUE 等。

CREATE TABLE Employee (
    EmployeeID INT,
    Name VARCHAR(100),
    Age INT,
    Salary DECIMAL,
    Email VARCHAR(100),
    DepartmentID INT,
    
    -- 主键约束
    CONSTRAINT pk_employee PRIMARY KEY (EmployeeID),
    
    -- 唯一约束
    CONSTRAINT uq_email UNIQUE (Email),
    
    -- 外键约束
    CONSTRAINT fk_department FOREIGN KEY (DepartmentID) REFERENCES Department (DepartmentID),
    
    -- 检查约束
    CONSTRAINT chk_age CHECK (Age >= 18),
    CONSTRAINT chk_salary CHECK (Salary > 0),
    
    -- 非空约束
    CONSTRAINT nn_name NOT NULL (Name)
);

修改表中的完整性约束

  1. 删除约束:使用 ALTER TABLEDROP CONSTRAINT 删除表中的完整性约束(如 CHECKPRIMARY KEYFOREIGN KEY 等)。

  2. 添加约束:使用 ALTER TABLEADD CONSTRAINT 添加新的完整性约束。

  3. 修改约束:不同的数据库管理系统(DBMS)对修改约束的支持不同,有些 DBMS 允许直接修改约束,有些则需要删除原有约束并添加新的约束

ALTER TABLE <表名>
DROP CONSTRAINT <约束名>;
ALTER TABLE Student
ADD CONSTRAINT C4 CHECK (birthdate <= '2005-01-01');

*5.6 域的完整性限制(不需要上,故这没有内容)

思考:如何表达更具一般性的约束?例如限制选课人数。
断言功能CREATE ASSERTION

5.7 触发器

两张表有外键约束,问是否能更新学生表的学号 ?

Update student setSno='Y71814006where Sno='199912104'

默认处理是拒绝【可以在设置里更改-关系】

在这可以改成级联操作

两张表没有外键约束,如何做到级联更新?

Update student set Sno='Y71814006'

where Sno='2104'

Update score set Sno='Y71814006'

where Sno='2104'

一直这样写不方便

-- 创建触发器:更新学生表的学号信息后,成绩表的学号一起更新器
--定义触发器:
create trigger 级联更新
on tStud  -- 触发器作用于 tStud 表
for update  -- 仅在执行 UPDATE 操作时触发
as
    update tScore
    set 学号 = (select 学号 from inserted)  -- 使用 inserted 表中的学号(更新后的学号)
    where 学号 = (select 学号 from deleted);  -- 使用 deleted 表中的学号(更新前的学号)
 -- 更新 tScore 表中与 tStud 表更新前学号相同的学号, 
-- 将其更新为 tStud 表中更新后的学号

-- 激活触发器: 
Update tStud 
set 学号 = '2000102103'  -- 设置新学号
where 学号 = '1999102103';  -- 在学号为 '1999102103' 的记录中进行更新

-- 该更新操作会触发 "级联更新" 触发器,自动更新 tScore 表中的学号

触发器(Trigger)

触发器是数据库中的一种特殊对象,允许在特定事件发生时自动执行一些操作。

通常是用户自定义的,并且是由事件驱动的。

数据库管理系统会在某些特定条件下自动激活触发器,执行一段预定义的操作或过程。

触发器特点:保存在数据库服务器中

触发器是定义在数据库中的对象。它们是存储在数据库服务器中的,因此一旦创建,它们会一直存在,直到被删除或修改。

触发器由数据库管理系统(DBMS)维护,无需每次操作时手动执行。

触发器的类型

按照触发事件的语言分类

DML触发器(Data Manipulation Language Trigger)

DML触发器是指那些在数据操作语句(如 insert、update 和delete)对数据表进行修改时自动触发的触发器。DML触发器主要用于监控和控制数据库中的数据修改操作

DDL触发器(Data Definition Language Trigger)

DDL触发器是指那些在对数据库对象(create、 alter和drop表、视图、索引、存储过程等)执行数据定义语言(DDL)操作时自动触发的触发器。DDL触发器通常用于数据库结构的监控和管理

5.7.1 定义与激活触发器

触发器又叫做事件-条件-动作(event-condition-action)规则  

当特定的系统事件发生时,对规则的条件进行检查,如果条件成立则执 行规则中的动作,否则不执行该动作。       触发器是一个能自动执行的特殊的存储过程

CREATE TRIGGER <触发器名>   
    {BEFORE | AFTER} <触发事件> ON <表名> 
    REFERENCING NEW | OLD ROW AS <变量> 
    FOR EACH {ROW | STATEMENT} 
    [WHEN <触发条件>]<触发动作体>;

CREATE TRIGGER:用来定义一个新的触发器。

<触发器名>:指定触发器的名称。应唯一

触发器和它所作用的表必须处于同一数据库模式(schema)下,确保触发器在指定的表上能够执行。

BEFORE:触发器在触发事件发生之前执行。

AFTER:触发器在触发事件发生之后执行。

<触发事件>:触发事件通常是 INSERTUPDATEDELETE,指定触发器响应的操作类型。

BEFORE INSERT:在插入数据之前触发。

AFTER INSERT:在插入数据之后触发。

<表名>:指定触发器作用的表,触发器将会在该表的触发事件发生时被激活。

REFERENCING NEW | OLD ROW AS <变量>

NEW:指代被插入或更新后的新数据行。在 INSERTUPDATE 事件中使用。

OLD:指代被删除或更新之前的数据行。在 DELETEUPDATE 事件中使用。

<变量>:定义 NEWOLD 数据行的别名。这个别名可以在触发器体内引用。例如,你可以使用 NEW 来访问新插入的数据或更新后的数据,使用 OLD 来访问删除前或更新前的数据。

FOR EACH {ROW | STATEMENT}

【行级触发器ROW:表示触发器在每一行数据的变动时执行一次。如果是批量操作(例如更新多个行),那么触发器会为每一行数据分别执行

【语句级触发器】STATEMENT:表示触发器只在整个 SQL 语句执行时触发一次,而不是针对每一行。

WHEN <触发条件>:可选的触发条件,指定一个条件表达式,只有在该条件为 TRUE 时,触发器才会执行。这个条件表达式可以基于 NEWOLD 行中的列值,也可以是其他的逻辑表达式。

<触发动作体>:这是触发器执行的实际操作。可以包含任何合法的 SQL 语句,如更新其他表的数据、插入日志记录、发送通知等。

书上给的是标准sql【知道即可,SqlServer中不是这样,比这个简单】

SQL SERVER (T-SQL)语法格式

CREATE TRIGGER 触发器名
ON 数据表名
[WITH ENCRYPTION]
FOR | AFTER | INSTEAD OF [INSERT] [, UPDATE] [, DELETE]

WITH ENCRYPTION(可选):这部分是对触发器定义文本的加密。加密后,触发器的定义(包括触发器的 SQL 代码)不能直接查看。通常用于保护触发器的逻辑,防止未授权的访问和修改。并非所有数据库系统都支持此功能, SQL Server 支持这个选项

FOR | AFTER | INSTEAD OF:

FOR:触发器会在指定的触发事件之前执行(如 BEFORE)。

AFTER:触发器会在指定的触发事件之后执行(如 AFTER)。

INSTEAD OF:触发器会代替指定的触发事件执行。这意味着触发器的操作将代替正常的 INSERTUPDATEDELETE 操作,通常用于处理复杂的逻辑或实现视图更新。

INSERT:在数据插入时触发。

UPDATE:在数据更新时触发。

DELETE:在数据删除时触发

after类 型触发器(for)

后触发触发器

“触发事件”成功执行完毕后,“被触 发事件”执行的触发器

after (for)触发器“触发事件”和“被 触发事件”都执行

after类型的触发器只能创建在数据表上,而不能创建在视图上

一张数据表可以创建多个因同一触发操作而生成 的触发器(执行顺序随机

建立一个更新触发器,当更新学生表后,输出 “数据已完成修改”,并显示学生表信息。

Create trigger update_学生  on tStud 
after update 
as  
print '数据已完成修改' 
select * from tStud

inserted表和deleted表

是系统为每个触发器准备的临时表,存放于内存中 

inserteddeleted 不是数据库中的物理表,而是 临时表

  • inserted:存放 INSERT 操作时的插入数据。

  • deleted:存放 DELETEUPDATE 操作时被删除的或旧的记录数据。

inserted表和deleted表中的记录只能查看,不能修改, 当触发器执行完毕后,与之相关的临时表也随之删除

CREATE TRIGGER 级联删除
ON tStud
AFTER DELETE
AS
    -- 使用 deleted 表获取删除的学生信息
    DELETE FROM tScore
    WHERE 学号 IN (SELECT 学号 FROM deleted)

没有单独的 updated

 UPDATE 操作 本质上是先执行一个 DELETE 操作再执行一个 INSERT 操作,所以触发器中会同时使用 deleted 表和 inserted 表来处理更新操作。 

 当一个 UPDATE 事件发生时:

  • deleted 存储的是更新前的旧数据(即更新之前的值)

  • inserted 存储的是更新后的新数据(即更新之后的值)

 练习:创建触发器,更新学生表的学号信息时,成绩表的学号 一起更新。

CREATE TRIGGER 级联更新
ON tStud
FOR UPDATE
AS
    UPDATE tScore-- 更新成绩表中的学号,使其与学生表中学号保持一致
    SET 学号 =(select 学号 from inserted)
    WHERE 学号 =(select 学号 from deleted)


instead of类型触发器

替换触发器

系统并不 执行“触发事件”的具体操作(比如insert、update或者 delete数据),而是直接执行“被触发事件”                                不会执行触发事件中的操作

可以定义在表上和视图上。对于每个触发操作(insert、update或delete)只能定义一个instead of类型触发器

CREATE TRIGGER 替代触发器
ON tStud
INSTEAD OF DELETE
AS
    PRINT '前面的触发事件中的删除没有执行';    -- 打印

    -- 显示删除操作涉及的记录(被删除的数据)
    SELECT * FROM deleted;
    -- 显示当前学生表中的所有数据
    SELECT * FROM tStud;
    
 
    DELETE FROM tStud WHERE 学号 = '993502235';

5.7.2 修改与查看触发器

使用sp_rename命令重命名触发器

sp_rename 'oldname', 'newname';

查看触发器

1. 使用 sp_helptrigger 存储过程查看触发器信息

sp_helptrigger [@tablename =] 'table'
                [, [@triggertype =] 'type'];

@tablename:要查看触发器的表名。如果你指定了表名,存储过程将显示该表上的所有触发器。

@triggertype:指定触发器的类型

EXEC sp_helptrigger 'tstud', 'INSERT';

查看 tstud 表上的 INSERT 类型触发器 

2. 使用系统存储过程sp_helptext查看触发器代码

sp_helptext 'trigger_name';

'trigger_name':指定触发器的名称,使用单引号括起来。

EXEC 是 SQL Server 中的一个命令,用于执行存储过程或动态 SQL 查询。它可以用来调用存储过程、执行 SQL 语句或执行其他批处理操作。

3. 使用系统存储过程sp_help查看触发器其他信息 

sp_help ‘trigger_name’

 禁用或启用触发器

ALTER TABLE 触发器所属表名称
{ENABLE|DISABLE}TRIGGER {ALL|触发器名称[,...n] }

 暂时禁用触发器Trig_stuDML的使用。

ALTER TABLE 学生 DISABLE TRIGGER Trig_stuDML

变 量

DECLARE @变量名 数据类型 [,...n]

@变量名:变量名,必须以 @ 开头。

数据类型:变量的数据类型,INTVARCHARDATE 等。可一次性声明多个变量,用逗号分隔

 局部变量在声明时并没有初始化,默认值为 NULL,即如果变量没有被赋值,它的值是 NULL。在 SQL Server 中,NULL 是一个特殊的值,表示“没有值”或“未知值”

局部变量可以使用 SETSELECT 语句为其赋值

SET @变量=表达式
SELECT @变量=表达式 [,…n]


SELECT @name=‘刘伟’,@age=21
select语句不能同时完成赋值和查询两种操作,set一次只能赋值一个

局部变量的显示

语法格式为:
PRINT 表达式
SELECT 表达式 [,…n] 
实例3:显示@age、@name、@msg的值。
PRINT @name
PRINT @age
SELECT @name as 姓名, 年龄=@age, @msg 欢迎词

5.7.3 删除触发器

DROP TRIGGER <触发器名> ON <表名>;

触发器必须是一个已经创建的触发器,并且只能由具有相应权限的用户删除

;