1、创建和管理视图
(1)认识视图
视图是以表为基础,通过SELECT语句定义所形成的一个虚拟表,它是用户查看数据库中数据的一种方式。同表一样,视图包含一系列带有名称的列数据和行数据,这些行和列的数据来自被查询的表。在视图中被查询的表称为视图的基表。
视图既可以看作一个虚拟表,也可以看作是一个查询的结果。视图所对应的数据并不实际地以视图结构存储在数据库中,而是存储在视图所引用的表中。
视图具有如下特点:视图的建立和删除不会影响基表;通过视图对数据进行的插入、删除和更新操作会影响基表;当视图来自多个基表时,不允许插入或删除数据行。若基表的数据发生变化,会自动地反映到视图中。
(2)视图的作用
视图通常用来集中、简化和自定义每个用户对数据库的不同认识。因此,视图具有以下方面的作用。
1)聚焦特定数据。允许用户通过视图访问数据,但不授予用户直接访问基表的权限,以此提高数据的安全性。
2)定制用户数据。对不同级别的用户,创建不同的视图,用户只能查看和操作与自己相关的数据。
3)合并分离数据。视图不仅可以显示来自基表的部分行数据、显示来自基表的部分列数据、显示来自多个表的部分数据,还可以显示来自基表的汇总统计数据,但原数据库的结构保持不变。
4)简化用户操作。通过视图用户不必书写复杂的查询语句就可对数据进行查询
(3)创建视图的方法
用户创建的视图可以基于表,也可以基于其他的视图。在SQL Server中,只能在当前数据库中创建视图,但是被新创建的视图所引用的表或视图可以在不同的数据库中。创建视图与创建数据表一样,可以使用两种方法:一是在对象资源管理器中通过图形化的工具创建,二是通过T-SQL语句来创建。
2、使用对象资源管理器创建视图
【例】 在XSCJ数据库的"XSB"表上,创建一个名为"V_学生信息"的视图,该视图用于显示人工智能与大数据学院女生或智能制造与汽车学院女生的"学号""姓名""性别""所属院系""专业名称",要求视图中的列名显示为"学生学号""学生姓名""性别""二级学院""专业"。
1)在"对象资源管理器"窗口依次展开"XSCJ"数据库中的"XSB"表,定位到"视图"节点。
2)右击"视图"节点,在弹出的快捷菜单中选择"新建视图"命令,出现如图所示的对话框。

3)选择要定义视图的基表"XSB",单击"添加"按钮,然后再单击"关闭"按钮。
4)在"数据源关系图"窗格中,勾选XSB表的"学号""姓名""性别""所属院系""专业名称"字段后,会逐一显示在"数据源列表"窗格中。
5)在"数据源列表"窗格中,如图所示,先设置视图的别名;然后再在"筛选器"列中的对应行设置查询条件"=人工智能与大数据学院"和"=0",这两个条件之间是"与"(AND)的关系;在"或..."列中的对应行设置条询条件"=通信工程学院"和"=0",这两个条件之间也是"与"(AND)的关系;"筛选器"列和"或..."列之间是"或"(OR)的关系。

6)设置完成后,在"T-SQL查询命令"窗格中会显示SELECT语句,也就是视图所要存储的查询语句。
7)右击窗口任意空白处,在弹出的快捷菜单中选择"执行SQL"命令,SELECT语句的执行结果会在"查询结果"窗格中显示,如上图所示。
8)单击工具栏中的"保存"按钮,在弹出的对话框中输入视图名称,再单击"确定"按钮即完成创建视图的操作。
提示:由于视图在使用方式上与表没有什么区别,因此为了区分视图和表,通常在命名视图时加前缀V_或VIEW_。
3、认识CREATE VIEW语句
CREATE VIEW语句用于创建视图,其基本语法形式如下。

其中,各参数含义如下。
1)列名:用于指定新建视图中要包含的列名。该列名可省略,如果未指定列名,则视图的列名与SELECT语句中的列具有相同的名称。但在下列情况下需要明确指定视图中的列名。
- 列是从算术表达式、函数或常量派生的。
- 在多表连接中有几个同名的列作为视图的列。
- 需要在视图中为某个列使用新的名字。
2)WITH ENCRYPTION:对视图的定义进行加密,保证视图的定义不被非法获得。
3)SELECT语句:用于定义视图的语句,以便从源表或另一视图中选择列和行构成新视图的列和行。但是在SELECT语句中,不能使用INTO关键字,不能使用临时表或表变量。另外,使用ORDER BY子句时,必须保证SELECT语句中的选择列表中有TOP子句。
4)WITH CHECK OPTION:表示在对视图进行INSERT、UPDATE和DELETE操作时要保证插入、更新或删除的行满足视图定义中设置的条件。
使用CREATE VIEW语句创建视图
【例】创建一个名为"V_不及格学生信息"的视图,该视图包含所有有不及格记录的学生的学号、姓名和原始成绩。

|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| USE XSCJ GO CREATE VIEW V_不及格学生信息(学号,姓名,原始成绩) AS SELECT DISTINCT XSB.学号,姓名,成绩 FROM XSB,CJB WHERE XSB.学号=CJB.学号 AND CJB.成绩<60 GO SELECT * FROM V_不及格学生信息 |

4、使用系统存储过程查看视图定义的文本
创建视图后,在实际工作中,可能需要查看视图定义,以了解数据从源表中的提取方式。如果想要查看有关视图的定义文本,可以使用sp_helptext系统存储过程。
查看XSCJ数据库中的"V_不及格学生信息"视图的定义。

|----------------------------|
| EXEC sp_helptext V_不及格学生信息 |
5、使用对象资源管理器修改视图
【例】 修改"V_不及格学生信息"视图,要求在视图中增加"课程名"的信息。其操作步骤如下。
1)在"对象资源管理器"中依次展开"XSCJ"数据库下的"视图"节点,定位到"V_不及格学生信息"视图节点。
2)右击"V_不及格学生信息"视图,在弹出的快捷菜单中选择"设计"命令,出现如图所示的窗口。

3)右击"数据源关系图"窗格,在弹出的快捷菜单中选择"添加表"命令,从出现的"添加表"对话框中选择"KCB"表,单击"添加"按钮,再单击"关闭"按钮。
4)在"数据源关系图"窗格中,勾选"KCB"表中的"课程名"。
5)单击工具栏中的"保存"按钮即可完成视图的修改。

6、创建和管理索引
用户对数据库最频繁的操作是进行数据查询。一般情况下,数据库在进行查询操作时需要对整个表进行数据检索。当表中的数据很多时,检索数据就需要很长的时间,这就造成了服务器的资源浪费。为了提高检索数据的能力,数据库引入了索引机制。
(1)认识索引
索引的含义:
索引是对数据库表中一个或多个列的值进行排序的结构,它由该表的一列或多个列的值及指向这些列值对应记录存储位置的指针所组成,是影响数据库性能的一个重要因素,由数据库进行管理和维护。索引是依赖于表建立的,它提供了数据库中编排表中数据的内部方法。一个表的存储由两部分组成,一部分用来存放表的数据页面,另一部分用于存放索引页面。索引就存放在索引页面上,通常,索引页面相对于数据页面来说小得多。当进行数据检索时,系统会先搜索索引页面,从中找到所需数据的指针,再直接通过指针从数据页面中读取数据。数据库使用索引的方式与书的目录很相似,它是表中数据和相应存储位置的列表,是一种树状结构,目的是用来加快访问数据表中的特定信息。
(2)索引的利弊
索引的建立有利也有弊,建立索引可以提高查询速度,但过多地建立索引会占据很多的磁盘空间。所以在建立索引时,数据库管理员必须权衡利弊,考虑让索引带来的有利效果大于带来的不利因素。通常情况下,对于经常被查询的列、在ORDER BY子句中使用的列、主键或外键列等可以建立索引,而对于那些在查询中很少被引用的列则不适合建立索引,特别是当UPDATE的性能需求远大于SELECT的性能需求时不应该创建索引。
(3)索引的分类
在SQL Server中,索引可以分为聚集索引、非聚集索引、唯一索引、复合索引、包含性列索引、视图索引、全文索引和XML索引。
(4)使用对象资源管理器创建索引
在SQL Server中,有两种情况是由系统自动创建索引:一种是设置了主键约束的列上系统会自动创建一个唯一聚集索引,另一种是设置了唯一约束的列上系统会自动创建一个唯一非聚集索引,除此以外就只能手动创建索引了。
(5)使用对象资源管理器创建索引
已知XSB表中的"学号"列已被设置为主键,请在"姓名"和"所属院系"两列上创建名为"ix_name_xi"的索引,按姓名降序排列。
分析:根据题意,XSB表上已将"学号"设置为主键,即系统自动创建了一个唯一聚集索引,另外,由于在同一个院系可能存在同名的情况,所以,本题要创建的索引只能是一个非唯一的非聚集的索引
其操作步骤如下。
1)在"对象资源管理器"窗口依次展开"XSCJ"数据库中的"XSB"表,定位到"索引"节点。
2)右击"索引"节点,在弹出的快捷菜单中选择"新建索引"→"非聚集索引"命令

3)在对话框的"索引名称"框中输入"ix_name_xi"。
4)单击"添加"按钮,在打开的对话框中勾选"索引键列"。
5)单击"确定"按钮,返回"新建索引"对话框,在"索引键列"列表中,设置"姓名"的排序顺序为"降序"。
6)单击"确定"按钮,创建索引完成。
另外,用户还可以通过选择对话框左列的"选项"来设置索引的参数,通过"包含行列"选项来为索引添加非键值辅助列,通过"存储"选项来选择索引存储文件组等参数。
(6)认识CREATE INDEX语句
使用CREATE INDEX语句可以创建出符合自己需要的索引。使用这种方法,通过选项可以指定索引类型、唯一性、包含性和复合性。可以创建聚集索引和非聚集索引,既可以在一个列上创建索引,也可以在两个或两个以上的列上创建索引。
CREATE INDEX语句的基本语法形式如下。

其中,<索引选项>为以下属性的组合。

以上各参数的说明如下。
- UNIQUE:用于创建唯一索引,此时SQL Server不允许数据行中出现重复的索引值。
- CLUSTERED:用于创建聚集索引。创建聚集索引时,表中数据行会按照聚集索引指定的物理顺序进行重排,因此最好在创建表时创建聚集索引。如果在CREATE INDEX语句中没有指定CLUSTERED选项,则默认使用NONCLUSTERED选项,创建一个非聚集索引。
- NONCLUSTERED:用于创建一个非聚集索引。
- FILLFACTOR:用于用户指定填充因子,可以设置为从0到100。
- PAD_INDEX:用于指定索引填充。PAD_INDEX选项只有在指定了FILLFACTOR时才有用,因为PAD_INDEX使用由FILLFACTOR所指定的百分比。
- SORT_IN_TEMPDB:指定是否在Tempdb数据库中存储临时排序的结果。
- IGNORE_DUP_KEY:用于指定在唯一索引中出现重复键值时的错误响应方式,也就是"忽略重复的值"的设置。当执行创建重复键的INSERT语句时,如果没有为索引指定IGNORE_DUP_KEY选项,SQL Server会发出一条警告消息,并回滚整个INSERT语句;而如果为索引指定了IGNORE_DUP_KEY选项,则SQL Server将会只发出警告消息而忽略重复的行。
- STATISTICS_NORECOMPUTE:设置是否自动重新计算统计信息。
- DROP_EXISTING:用于在创建索引时删除并重建指定的已存在的索引,如果指定的索引不存在,系统会给出警告消息。
(7)使用CREATE INDEX语句创建索引
【例】 在XSB表的"姓名"列上创建一个非聚集索引,索引名为"IX_XSB_XM",并降序排列。

【例】 为CJB表创建非聚集索引IX_CJB_学号_课程号,该索引包括学号和课程号两个索引列,均按升序排列。

说明:由于在创建索引时,默认情况下创建的都是非聚集索引,所以NONCLUSTERED可以省略不写。
(8)查看索引信息
查看索引信息的两种方法如下。
一是利用"对象资源管理器"查看索引信息。在该窗口依次展开"XSCJ"数据库中的"XSB"表,定位到"索引"节点,右击要查看的索引,选择"属性"命令,即可查看索引信息。
二是使用系统存储过程sp_helpindex查看索引信息。其语法格式如下。

使用系统存储过程查看成绩表CJB的索引信息如图所示。结果集中包括3列:index_name表示索引的名称;index_description为索引说明;index_keys列出了索引列。

(9)删除索引
当不再需要某个索引时,可以将它从数据库中删除,删除索引可以收回索引所占用的存储空间,供其他数据库对象使用。删除索引的两种方法如下。
一是使用"对象资源管理器"删除索引。右击要删除的索引,选择"删除"命令,在出现的对话框中,单击"确定"按钮即可删除索引。
二是使用DROP INDEX语句删除索引。如果要删除CJB表中的索引,在查询窗口中输入"DROP INDEX XSB.IX_CJB_学号_课程号"语句并执行即可。
7、创建和管理存储过程
(1)认识存储过程
存储过程是一组T-SQL语句的预编译集合,它以一个名称存储并作为一个单元处理,能实现特定的功能。存储过程可接收参数、输出参数,返回单个或多个结果集,由应用程序通过调用执行。
(2)存储过程的优点
存储过程是一种独立的数据库对象,它在服务器上创建和运行,与存储在客户机的本地T-SQL语句相比,具有以下优点。
模块化程序设计
一个存储过程就是一个模块,可以用它来封装并实现特定的功能。存储过程一旦创建,以后即可在程序中被调用任意多次。可以改进应用程序的可维护性,并允许应用程序统一访问数据库。
提高执行效率,改善系统性能。
系统在创建存储过程时会对其进行分析和优化,并在首次执行该存储过程后将其驻留于高速缓冲存储器中,以加速该存储过程的后续执行。
减少网络流量
一个原本需要从客户端发送数百行T-SQL语句的操作,通过创建存储过程后,只需一条执行存储过程的语句即可实现相同的功能,而不必在网络中发送数百行代码。
提供了一种安全机制
用户可以被授予权限来执行存储过程,而不必直接对存储过程中引用的对象具有权限。因此,通过创建存储过程来完成插入、修改、删除和查询等操作,可以实现对表和视图等对象的访问控制。
SQL Server系统中已经提供了许多存储过程,它们用于系统管理、用户登录管理、数据库对象管理、权限设置和数据复制等操作,称其为系统存储过程;用户自己创建的存储过程,则称为用户存储过程。本章主要介绍用户存储过程的创建、执行和删除等操作。
(3)认识存储过程的创建和执行方法
1)创建存储过程
在SQL Server中,创建存储过程的语法格式如下。

其中,各参数的说明如下。
- 分组号:是可选的整数,用来区分一组同名存储过程中的不同对象,以便将来用一条DROP PROCEDURE语句即可将同组的过程一起删除。例如,创建了名为"MyProc;1"和"MyProc;2"的存储过程,可以使用DROP PROCEDURE MyProc语句将它们一起删除。
- @参数:是存储过程中的参数。参数包括输入参数和输出参数,其中输入参数用于提供执行存储过程所必需的参量值,输出参数用于返回执行存储过程后的一些结果值。用户必须在执行存储过程时提供每个所声明参数的值,除非定义了该参数的默认值。参数其实是局部变量,只在声明它的存储过程内有效,因此在其他存储过程中可以使用同名参数。
- 默认值:是输入参数的默认值。如果定义了默认值,不必指定该参数的值即可执行过程。默认值必须是常量或NULL,但是如果存储过程将对输入参数使用LIKE关键字,那么默认值中可以包含通配符(%、_、[]和[^])。
- OUTPUT:表明参数是输出参数。使用输出参数可将执行结果返回给过程的调用方。
- WITH RECOMPILE:表示SQL Server不在高速缓存中保留该存储过程的执行计划,而在每次执行时都对它进行重新编译。
- WITH ENCRYPTION:表示对存储过程的文本进行加密,防止他人查看或修改。
- FOR REPLICATION:表示创建的存储过程只能在复制过程中执行而不能在订阅服务器上执行。
- T-SQL语句:用于定义存储过程执行的各种操作。
创建存储过程的时候要考虑以下几个因素。
- 创建存储过程时可以参考表、视图或其他存储过程。
- 如果在存储过程中创建了临时表,那么该临时表只在该存储过程执行时有效,当存储过程执行完毕,临时表就消失。
- 在一个批命令中,CREATE PROCEDURE语句不能与其他的T-SQL语句混合使用,需要在它们之间加入GO命令。
- 存储过程可以嵌套调用,但最多不能超过32层,当前嵌套层的数据值存储在全局变量@@nestlevel中。如果一个存储过程调用另一个存储过程,那么内层的存储过程可以使用外层存储过程所创建的全部对象,包括临时表。
2)执行存储过程
在SQL Server中执行存储过程的语法格式如下。

其中,各参数的说明如下。
- @状态值:是一个可选的整型变量,用于保存存储过程的返回状态。这个变量在用于EXECUTE语句时,必须已在批处理、存储过程或函数中声明。
- @参数:是在创建存储过程时定义的参数。当使用该选项时,各参数的枚举顺序可以与创建存储过程时的定义顺序不一致,否则两者顺序必须一致。
- 参量值:是存储过程中输入参数的值。如果没有指定参数名称,参量值必须按创建存储过程时的定义顺序给出。如果在创建存储过程时指定了参数的默认值,执行时可以不再指定。
- @变量:用来存储参数或返回参数的变量。当存储过程中有输出参数时,只能用变量来接收输出参数的值,并在变量后加上OUTPUT关键字。
- OUTPUT:用来指定参数是输出参数。该关键字必须与"@变量"连用,表示输出参数的值由变量接收。
- DEFAULT:表示参数使用定义时所指定的默认值。
- WITH RECOMPILE:表示执行存储过程时强制重新编译。
(4)创建和执行简单的存储过程
【例】创建一个不包含任何参数的存储过程P_CJ1,查询XSCJ库中成绩在90分以上的学生的学号、姓名、课程名和成绩信息,按成绩降序排列,并执行该存储过程。


|-------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE PROCEDURE P_CJ AS SELECT XSB.学号,姓名,成绩 FROM XSB,CJB,KCB WHERE XSB.学号=CJB.学号 AND KCB.课程号=CJB.课程号 AND 成绩>=10 ORDER BY 3 DESC GO EXEC P_CJ2 |
(5)创建和执行带输入参数的存储过程
【例】 创建一个带有输入参数的存储过程P_CJ,查询指定课程号(作为输入参数)的学生成绩信息,并用三种方式执行该存储过程
|--------------------------------------------------------------------------------------------------------------------------------------|
| CREATE PROC P_CJ @KCH CHAR(4)='1005' AS SELECT 学号,课程号,成绩 FROM CJB WHERE 课程号=@KCH GO EXEC P_CJ EXEC P_CJ '1006' EXEC P_CJ @KCH='1007' |
(6)创建和执行带输入参数和输出参数的存储过程
【例】 创建带有输入参数和输出参数的存储过程P_KCH,返回指定教师(作为输入参数)所授课程的课程号(作为输出参数),并执行该存储过程。

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE PROC P_KCH @skjs varchar(10), @kch char(4) OUTPUT AS SELECT @kch=课程号 FROM KCB WHERE 授课教师=@skjs GO DECLARE @skjs varchar(10),@kch char(4) SET @skjs='于谦' EXEC P_KCH @skjs,@kch OUTPUT PRINT @skjs + '老师所受课程号为:' +@kch |
(7)查看存储过程信息
查看用户定义的存储过程信息有两种方法。
一是在"对象资源管理器"中,展开目标数据库中的"可编程性"节点,定位到"存储过程"节点;右击要查看的存储过程名,选择"属性"命令,打开"存储过程属性"对话框,即可查看其相关信息。
二是使用系统存储过程来查看用户定义的存储过程的相关信息,具体步骤如下。
1)查看存储过程的定义,即查看用于创建存储过程的T-SQL语句。这对于没有创建存储过程的T-SQL脚本文件的用户是很有用的。其语法格式如下。

2)获得有关存储过程的信息,如存储过程的架构、创建时间及其参数等。其语法格式如下。

3)查看存储过程的依赖关系,即列出存储过程所使用的对象和调用该存储过程的其他过程。其语法格式如下。

(8)删除存储过程
删除存储过程有两种方法。
一是在"对象资源管理器"中依次展开"XSCJ"数据库中的"可编程性"节点,定位到"存储过程"节点,右击要删除的存储过程,选择"删除"命令,在出现的对话框中,单击"确定"按钮即可完成删除操作。
二是使用DROP PROCEDURE语句删除存储过程,其语法格式如下。

删除存储过程的时候要考虑以下几个因素。
- DROP PROCEDURE语句可删除一个或多个存储过程或存储过程组,但不能删除存储过程组中的某个存储过程。
- 为确定将要删除的存储过程是否被其他存储过程嵌套调用,可查看它的依赖关系。
8、创建和管理触发器
(1) 认识触发器
SQL Server提供了两种主要机制来强制使用业务规则和数据完整性:约束和触发器。触发器是一种特殊类型的存储过程,包括三种类型的触发器:DML触发器、DDL触发器和登录触发器。当数据库中发生DML事件(数据操作语言事件,如INSERT、UPDATE、DELETE)时,将调用DML触发器。当服务器或数据库中发生DDL事件(数据定义语言事件,如CREATE、ALTER、DROP)时,将调用DDL触发器。当发生LOGON事件(用户与SQL Server实例建立会话)时,将调用登录触发器。下面将重点介绍DML和DDL触发器的使用。
触发器具有以下优点。
- 触发器自动执行。触发器不需要调用,是通过事件进行触发而自动执行的。
- 触发器可以强制实现数据完整性。触发器包含了使用T-SQL语句的复杂处理逻辑,不仅支持约束的所有功能,还可以实现更为复杂的数据完整性约束。例如,它可以实现比CHECK约束更复杂的功能。CHECK约束只能根据逻辑表达式或同一表中的列来验证列值。如果应用程序要求根据另一个表中的列来验证列值,则必须使用触发器。
- 触发器可以通过数据库中的相关表实现级联更改。需要注意的是,尽管触发器是一种功能强大的工具,但在某些情况下并不总是最好的方法。约束的执行优先于触发器。如果约束的功能完全能够满足应用程序的需求,就应该考虑使用约束。当约束无法满足要求时,触发器就极为有用。
(2)创建DML触发器
在SQL Server中创建DML触发器的语法格式如下。

其中,各参数的说明如下。
- WITH ENCRYPTION:像存储过程一样,也可以使用WITH ENCRYPTION选项对触发器的文本进行加密。
- FOR | AFTER:指定触发器只有在触发事件包含的所有操作都已成功执行后才被激活。所有的引用级联操作和约束检查也必须在激活此触发器之前成功完成。可以指定FOR,也可以指定AFTER。注意,不能在视图上定义AFTER触发器。
- INSTEAD OF:用该选项来创建触发器时,将用触发器中的SQL语句代替触发事件包含的SQL语句执行。
- INSERT、UPDATE、DELETE:指定在表或视图上用于激活触发器的操作类型。必须至少指定一个选项。在触发器定义中允许使用这些选项的任意组合。如果指定的选项多于一个,需用逗号分隔这些选项。
- NOT FOR REPLICATION:表示当复制代理修改涉及触发器的表时,不会激活触发器。
- T-SQL语句:用于定义触发器执行的各种操作
DML触发器和触发它的语句,被视为单个事务,可以在触发器中回滚该事务,如果检测到严重错误(例如,磁盘空间不足),整个事务会自动回滚。
DML触发器最常见的应用是为表的修改设置复杂的规则。当表的修改不符合触发器设置的规则时,触发器就应该撤销对表的修改操作。此时,可以使用ROLLBACK TRANSACTION语句。ROLLBACK TRANSACTION语句不生成显示给用户的信息,如果在触发器中需要发出警告,可使用RAISERROR或PRINT语句。
(3)创建DDL触发器
在SQL Server中创建DDL触发器的语法格式如下。

创建DDL触发器的语法同创建DML触发器的语法非常相似,其中相同参数的含义不再赘述,不同参数的说明如下。
- ALL SERVER:表示将DDL触发器的作用域应用于当前服务器。
- DATABASE:表示将DDL触发器的作用域应用于当前数据库。
- event_type:执行之后将导致激活DDL触发器的T-SQL语言事件的名称,如CREATE_TABLE、ALTER_TABLE、DROP_TABLE等。
- event_group:预定义的T-SQL语言事件分组的名称。执行任何属于event_group的T-SQL语言事件后,都将激活DDL触发器。如DDL_TABLE_EVENTS语言事件分组涵盖CREATE TABLE、ALTER TABLE、DROP TABLE语句。DDL触发器用于在数据库中管理任务,如:审核和控制数据库操作。
(4)Inserted表和Deleted表的作用
每个触发器被激活时,系统都为它自动创建两张临时表:Inserted表和Deleted表。这两张表都是逻辑(概念)表,它们在结构上类似于定义触发器的表,其中Inserted表用于存储INSERT和UPDATE语句所影响的行的副本;Deleted表用于存储DELETE和UPDATE语句所影响的行的副本。触发器执行完成后,这两张临时表会自动被删除。
当执行INSERT操作时,新行被同时添加到触发器表和Inserted表中;当执行DELETE操作时,行从触发器表中删除,并被保存到Deleted表中;当执行UPDATE操作时,旧行被保存到Deleted表中,然后新行被复制到触发器表和Inserted表中。
(5)创建和激活INSERT触发器
【例】 为XSB表创建一个INSERT触发器,当插入的新行中"所属院系"的值不是"人工智能与大数据学院"时,就撤销该插入操作,使用RAISERROR语句返回错误信息。然后激活触发器以实现数据完整性。
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| USE XSCJ; GO CREATE TRIGGER T_INSERT_XSB ON XSB FOR INSERT AS DECLARE @ssyx varchar(20); SELECT @ssyx = 所属院系 FROM Inserted; IF @ssyx!= '人工智能与大数据学院' BEGIN ROLLBACK TRANSACTION; RAISERROR('不能插入非人工智能与大数据学院的学生信息!', 16, 10); END GO INSERT XSB(学号, 姓名, 性别, 出生日期, 所属院系, 专业名称) VALUES ('1903030091', '陈希', 1, '2001-8-12', '通信工程学院', '移动通信技术'); |
(6)创建和激活UPDATE触发器
【例】 为XSB表创建一个UPDATE触发器,当更新了某位学生的学号信息时,就用触发器级联更新CJB表中相关的学号信息。然后,激活触发器以实现数据完整性。
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE TRIGGER T_UPDATE_XSB ON XSB FOR UPDATE AS DECLARE @old char(10), @new char(10); SELECT @old = Deleted.学号, @new = Inserted.学号 FROM Deleted, Inserted WHERE Deleted.姓名 = Inserted.姓名; PRINT '准备级联更新 CJB 表中的学号信息...'; UPDATE CJB SET 学号 = @new WHERE 学号 = @old; PRINT '已经级联更新 CJB 表中原学号为' + @old + '的信息!'; GO UPDATE XSB SET 学号 = '1903029999' WHERE 学号 = '1903021691'; UPDATE XSB SET 学号 = '1903070404' WHERE 学号 ='1903070405' |
**提示:**由于CJB表中的"学号"列上有外键约束,而外键约束默认是强制约束(即不能修改XSB和CJB表中的学号值),如果要实现本例中的级联更新操作,需要将外键的强制约束设置为"否

(7)查看触发器信息
查看触发器信息有两种方法。
一是在"对象资源管理器"中,依次展开目标数据库中的目标数据表节点,定位到"触发器"节点,右击要查看的触发器,选择"查看依赖关系"命令,出现"对象依赖关系"对话框。在该对话框中选择"依赖于触发器的对象"或"触发器依赖的对象"单选项,可以看到对应的依赖关系。
二是使用系统存储过程来查看触发器的相关信息,具体内容如下。
1)查看触发器的定义,即查看用于创建触发器的T-SQL语句。这对于没有创建触发器的T-SQL脚本文件的用户是很有用的。其语法格式如下。

|-------------------------------|
| EXEC SP_HELPTEXT T_UPDATE_XSB |
2)获得有关触发器的信息,如触发器的所有者、创建时间等。其语法格式如下。
|-------------------|
| EXEC T_UPDATE_XSB |
3)查看触发器的依赖关系,即列出触发器所引用的对象和引用该触发器的其他触发器。其语法格式如下。
|------------------------------|
| EXEC sp_depends T_UPDATE_XSB |
4)查看在该表或视图上创建的所有触发器对象。其语法格式如下。
|------------------------------|
| EXEC sp_depends T_UPDATE_XSB |
(8)删除触发器
删除触发器有两种方法。
一是在"对象资源管理器"中,依次展开目标数据库中的目标数据表下的"触发器"节点,定位到要删除的触发器节点,右击该触发器,选择"删除"命令,在出现的对话框中,单击"确定"按钮即可完成操作。
提示:在对象资源中,不同类型的触发器所在的位置不同:

二是使用DROP TRIGGER语句删除触发器,其语法格式如下。

(9)禁用触发器
用户可以禁用或启用指定的触发器,比如:某个表上的DML触发器、某个数据库作用域的DDL触发器、在服务器作用域中的DDL触发器,如下所示。

9、事务的基本概念 (扩展)
数据库提供了增删改查等几种基础操作,用户可以灵活地组合这几种操作,实现复杂的语义。在很多场景下,用户希望一组操作可作为一个整体一起生效,这就是事务。事务就是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。所以说事务是数据库状态变更的基本单元,包含一个或多个操作(例如多条SQL语句)。
例如经典的转账事务,就包括 3个操作:①检查A账户余额是否足够。②如果足够,从A扣减 100元。③B账户增加 100元。
事务的特征
事务具有 4个基本特性:
原子性(Atomicity)、
一致性(Consistency)、
隔离性(Isolation)
持久性(Durability)。
这 4个特性也被简称为事务的ACID特性。
1)原子性
这一组操作要么一起生效,要么都不生效,事务执行过程中如遇错误,已经执行的操作要全部撤回,这就是事务的原子性。如果失败发生后,部分生效的事务无法撤回,那数据库就进入了不一致状态,就与真实世界的事实相左。例如转账事务:从A账户扣款100元后,转给B账户的时候失败了,B账户还未增加款项,如果A账户扣款操作未撤回,这个世界就莫名其妙丢失了 100元。原子性可以通过记日志(更改前的值)来实现,还有一些数据库将事务操作缓219存在本地,如遇失败,直接丢弃缓存里的操作。
2)一致性
数据库反映的是真实世界,真实世界有很多限制,例如:账户之间无论怎么转账,总额不会变等现实约束;年龄不能为负值,性别只能是男、女选项等完整性约束。事务执行,不能打破这些约束,保证事务从一个正确的状态转移到另一个正确的状态,这就是一致性。一致性既依赖于数据库实现(原子性、持久性、隔离性也是为了保证一致性),也依赖于应用端编写的事务逻辑。
3)隔离性
数据库为了提高资源利用率和事务执行效率、降低响应时间,允许事务并发执行。但是多个事务同时操作同一对象,必然存在冲突,事务的中间状态可能暴露给其他事务,导致一些事务依据其他事务中间状态,把错误的值写到数据库里。需要提供一种机制,保证事务执行不受并发事务的影响,让用户感觉,当前仿佛只有自己发起的事务在执行,这就是隔离性。隔离性让用户可以专注于单个事务的逻辑,不用考虑并发执行的影响。数据库通过并发控制机制保证隔离性。由于隔离性对事务的执行顺序要求较高,很多数据库提供了不同选项,用户可以牺牲一部分隔离性,提升系统性能。这些不同的选项就是事务隔离级别。
4)持久性
事务只要提交了,它的结果就不能改变了,即使遇到系统宕机,重启后数据库的状态与宕机前一致,这就是事务的持久性。数据只要存储在非易失存储介质上,如硬盘,宕机就不会导致数据丢失。
因此数据库可以采用以下两种方法来保证持久性:
①事务完成前,所有的更改都保证存储到磁盘上了。
②提交完成前,事务的更改信息,以日志的形式存储在磁盘,重启过程根据日志恢复出数据库系统的内存状态。一般而言,数据库会选择这种方法。
10、数据库并发控制 (扩展)
前面讲到事务是并发控制的基本单位,保证事务的ACID特性是事务处理的重要任务,而事务的ACID特性遭到破坏的原因之一是多个事务对数据库的并发操作造成的。为了保证事务的隔离性,DBMS需要对并发的事务进行正确的调度。下面将主要讨论对事务的并发操作导致哪些数据库的不一致性以及如何判断对并发事务的调度是正确的。
(1)并发操作带来的数据不一致性
考虑常见的火车售票系统的活动序列:
步骤 1:甲客户端(事务T1)读出某趟列车的二等座余票A,设A=20;
步骤 2:乙客户端(事务T2)读出同一趟列车的二等座余票A,也为 20;
步骤 3:甲客户端买了一张二等座的票,修改余票A← A-1,将A=19写回数据库;步骤 4:乙客户端也买了一张二等座的票,修改余票A← A-1,也将A=19写回数据库。
结果明明卖出了 2张二等座的票,数据库中的余票只减少了 1。这种情况称为数据库的不一致性。是由多个事务对相同的数据进行并发操作引起的。在并发操作的情况下,对T1、T2两个事务的操作序列的调度是随机的,若按上面的调度序列执行,则T1事务的修改就被丢失。这是由于步骤 4中T2事务修改A写回后覆盖了T1事务的修改。
并发操作带来的数据不一致性包括 3类:丢失修改、不可重复读和读"脏"数据。
1)丢失修改
丢失修改(Lost Updates)是指两个事务T1和T2读入同一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失。上面火车售票例子就属于此类。
2)不可重复读
不可重复读(Non-Repeatable Read)是指事务T1读取数据后,事务T2执行更新操作,使T1无法再现前一次读取结果。具体来讲,有 3种情况:
①事务T1读取某一数据后,事务T2对其进行了修改,当事务T1再次读取该数据时,得到与前一次不同的值。例如,T1读取B=100进行运算,T2读取了同一数据B,对其进行修改后将"B=200"写回数据库。T1为了对读取值校对重新读B,B已为 200,与第一次读取的值不同。
②事务T1按一定条件从数据库中读取某些数据记录后,事务T2删除了其中部分记录,当T1再次按相同的条件读取数据时,发现某些记录神秘地消失了。
③事务T1按一定条件从数据库中读取某些数据记录后,事务T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。
3)读"脏"数据
读"脏"数据(Dirty Read)是指事务T1修改某一数据,并将其写回磁盘,事务T2读取同一数据后,T1由于某种原因被撤销,这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,则T2读到的数据为"脏"数据,即不正确的数据。
产生上述 3类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制就是要用正确的方式调度并发的事务,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。
(2)事务调度的可串行性
为了保证隔离性,
一种方式是所有事务串行执行,让事务之间不互相干扰。但是串行执行效率非常低,为了增大吞吐,减小响应时间,数据库通常允许多个事务同时执行。
因此并发控制模块需要保证:事务并发执行的效果,与事务串行执行的效果完全相同,以达到隔离性的要求。
