一、过程化SQL的块结构
基本的SQL是高度非过程化的语言。嵌入式SQL将SQL语句嵌入程序设计语言,借助高级语言的控制功能实现过程化。过程化SQL是对SQL的扩展,使其增加了过程化语句功能。
过程化SQL程序的基本结构是块。所有的过程化SQL程序都是由块组成的。这些块之间可以相互嵌套,每个块完成一个逻辑操作。
1.过程化SQL块的基本结构图示:
2. 变量和常量的定义
变量定义
变量名 数据类型 [[NOT NULL] :=初值表达式]
或者
变量名 数据类型 [[NOT NULL] 初值表达式]
常量定义
常量名 数据类型 CONSTANT:=常量表达式
常量必须要给一个值,并且该值在存在期间或常量的作用域内不能改变。如果试图修改它,过程化SQL将返回一个异常。
赋值语句
变量名 :=表达式
3. 流程控制
过程化SQL提供了流程控制语句,主要有条件控制语句和循环控制语句。这些语句的语法、定义和一般的高级语言类似。
- 条件控制语句
1)IF语句
IF condition THENSequence_of_statements; /*条件为真时语句序列才被执行*/
END IF /*条件为假或NULL时什么也不做,控制转移至下一个语句*/
2)IF-THEN语句
IF condition THENSequence_of_statements1; /*条件为真时语句序列才被执行*/
ELSESequence_of_statements2; /*条件为假或NULL时才被执行*/
END IF
3)嵌套的IF语句
在THEN和ELSE子句中还可以包含IF语句,IF语句可以嵌套。
2. 循环控制语句
1)最简单的循环语句LOOP
LOOPSequence_of_statements; /*循环体,一组过程化SQL语句*/
END LOOP;
多数数据库服务器的过程化SQL都提供EXIT、BREAK或LEAVE等循环结束语句以保证LOOP语句块能够在适当的条件下提前结束。
2)WHERE-LOOP循环语句
WHERE condition LOOPSequence_of_statements; /*条件为真时执行循环体内的语句序列*/
END LOOP;
每次执行循环体语句之前首先对条件进行求值,如果条件为真则执行循环体内的语句序列,如果条件为假则跳过循环并把控制传递给下一个语句。
3)FOR-LOOP
FOR count IN [REVERSE] bound1...bound2 LOOPSequence_of_statements;
END LOOP;
FOR循环的基本执行过程:
将count设置为循环的下界bound1,检查它是否小于上界bound2。当指定REVERSE时则将count设置为循环的上界bound2,检查count是否大于下界bound1。如果越界则执行跳出循环,否则执行循环体,然后按照步长(+1或-1)更新count的值,重新判断条件。
3. 错误处理
如果过程化SQL在执行时出现异常,则应该让程序在产生异常的语句处停下来,根据异常的类型去执行异常处理语句。
SQL标准对数据库服务器提供什么样的异常处理作出了建议,要求过程化SQL管理器提供完善的一次处理机制。
二、存储过程和函数
过程化SQL块主要有命名块和匿名块。匿名块每次执行时都要进行编译,它不能被存储到数据库中,也不能在其他过程化SQL块中调用。过程和函数是命名块,他们被编译后保存在数据库中,称为持久性存储模块(PSM),可以 被反复调用,运行速度较快。
1.存储过程
存储过程是由过程化SQL语句书写的过程,这个过程经编译和优化后存储在数据库服务器中,因此称它为存储过程,使用时只要调用即可。
1. 存储过程的优点
1)由于存储过程不像解释执行的SQL语句那样在提出操作请求时才进行语法分析和优化工作,因而运行效率高,它提供了在服务器端快速执行SQL语句的有效途径。
2)存储过程降低了客户机和服务器之间的通信量。客户机上的应用程序只要通过网络向服务器发出调用存储过程的名字和参数,就可以让关系数据库管理系统执行其中的多条SQL语句并进行数据处理,只有最终的处理结果才返回客户端。
3)方便实施企业规则。可以把企业规则的运算程序写成存储过程放入数据库服务器中,由关系数据库管理系统管理,既有利于集中控制,又能够方便地进行维护。当企业规则发生变化时只要修改存储过程即可,无需修改其他应用程序。
2. 存储过程的用户接口
1)创建存储过程
CREATE OR REPLACE PROCEDURE 过程名([参数1,参数2,,,]) /*存储过程首部*/
AS <过程化SQL块> /*存储过程体,描述该存储过程的操作*/
存储过程包括过程首部和过程体,在过程首部,“过程名”是数据库服务器合法的对象标识;参数列表[参数1,参数2,]用名字来标识调用时给出的参数值,必须指定值的数据类型。
过程体是一个<过程化SQL块>,包括声明部分和可执行语句部分。
2)执行存储过程
CALL/PERFORM PROCEDURE 过程名([参数1,参数2,,,])
使用CALL或者PERFORM等方式激活存储过程的执行,在过程化SQL中,数据库服务器支持在过程体中调用其他存储过程。
3)修改存储过程
ALTER PROCEDURE 过程名1 RENAME TO 过程名2;(重新命名一个存储过程)
ALTER PROCEDURE 过程名 COMPILEL;(重新编译一个存储过程)
4)删除存储过程
DROP PROCEDURE 过程名();
2. 函数
函数必须指定返回的类型。
- 函数的定义语句格式
CREATE OR REPLACE FUNCTION 函数名([参数名1,参数名2,...]) RETURNS<类型> AS <过程化SQL块>
函数的执行语句格式
CALL/SELECT 函数名([参数名1,参数名2,...])
修改函数
重命名一个自定义函数
ALTER FUNCTION 函数名1 RENAME TO 函数名2
重新编译一个函数
ALTER FUNCTION 函数名 COMPILE;
三、过程化SQL中的游标
和嵌入式SQL一样,在过程化SQL中如果SELECT语句只返回一条记录,可以将该结果存放到变量中。当查询返回多条记录时,就要使用游标对结果集进行处理。一个游标与一个SQL语句相关联。在存储过程中可以定义普通 游标、REFCURSOR类型游标、带参数的游标等。
存储过程和自定义函数用的比较多,因此对存储过程和函数要加深了解。
四. 存储过程和触发器的区别?
触发器与存储过程的主要区别在于触发器的运行方式。存储过程必须有用户、应用程序或者触发器来显示的调用并执行,而触发器是当特定时间出现的时候,自动执行或者激活的,与连接用数据库中的用户、或者应用程序无关。
五. 存储过程和函数的区别?
存储过程是用户定义的一系列SQL语句的集合,而函数通常是数据库已定义的方法,具体区别如下:
1.对于存储过程来说可以返回参数,而函数只能返回值或者表对象.
2.函数必须有返回值,存储过程可有可无
3.存储过程一般是作为一个独立的部分来执行,而函数可以作为查询语句的一部分来调用
六.触发器
1、什么是触发器
触发器是一个在修改指定表中的数据执行的存储过程。通常通过创建触发器来强制实现 不同表 中的逻辑相关数据的引用完整性或一致性。由于用户不能绕过触发器,所以用来 强制实施复杂的业务规则!以此确保数据的 完整性 !
2、为什么要使用触发器
(1)触发器自动执行,他们在表的数据做了任何修改,之后立即激活!(和事件差不多了)
(2)触发器可以通过数据库中的相关表进行层叠更改,这直接把代码写在客户端更加合理!
(3)触发器的限制可以引用到其它表中的列!
3、触发器如何保证数据的完整性
个人理解:比如我们做牛腩新闻系统的时候,如果我们删除了一条新闻,那么删除这条新闻的时候,它下面的新闻的评论也随之删除,此时涉及到两个表,一个是新闻表,一个是评论表!如果没有触发器的话,我们在程序中还要加上删除新闻评论表中的对应的内容!但是有时候程序员万一要是忘记设计删除新闻评论类,那么数据就不完整了!(新闻表中没有了此类新闻,但是评论表中却有此类新闻的评论!)
4、触发器的种类
我们之前设计的触发器好像都是DML的,但是还有DDL,那么二者之间有何区别呢!
DML触发器(修改表中的数据用)
DML(Data Manipulation Language)触发器是当数据库中发生数据操作语言事件要执行的操作。它通常包含三种:INSERT触发器,UPDATE触发器,DELETE触发器!当我们要对 数据库里的数据 进行操作的时候,我们要用到DML触发器!
DDL触发器(修改表)
DDL触发器是当数据库中发生数据定义语言(Data Definition Language)主要包括CREATE,ALTER,DROP等操作!当我们用在 定义或者改变表的结构,数据类型,表之间的连接和约束的时候,我们就用DDL触发器!
后记:
触发器保证了我们数据库数据的完整性,DDL是修改表的层次,DML是修改表中的数据层次!
创建DML触发器
接下来看看例题,首先先创建两张表
CREATE TABLE [dbo].[Stu]([ID] [int] IDENTITY(1,1) NOT NULL,--自增长ID[Name] [varchar](50) NOT NULL,--学生姓名[subject] [varchar](50) NOT NULL,--学科[credit] [decimal](18, 2) NOT NULL--单科学分)
GOCREATE TABLE [dbo].[Stu_sum]([Name] [varchar](50) NULL,--学生姓名[number] [int] NULL--总学分
)
GO
现在有一张学生单科成绩表和一个成绩汇总表
INSERT触发器
要求插入一个学生单科成绩,并把分数汇总到Stu_sum 表中,根据学生的姓名(这里做测试没有做其他约束)
CREATE TRIGGER dbo.stu_TRIGGER_INSERTON dbo.StuAFTER INSERT
AS
BEGINSET NOCOUNT ON;--不返回影响行数--插入数据时会产生INSERTED表(表结构和Stu一样)--把[Stu_sum]存在的数据先更新掉update b set b.number=isnull(b.number,0)+a.credit from (select [Name],sum(credit) credit from INSERTED group by [Name]) ajoin [dbo].[Stu_sum] b on a.Name=b.Name--把不存在的添加进[Stu_sum]INSERT into [dbo].[Stu_sum](Name,number)select a.Name,a.creditfrom (select [Name],sum(credit) credit from INSERTED group by [Name]) aleft join [dbo].[Stu_sum] b on a.Name=b.Namewhere b.Name is null
END
GO
首先插入一条数据
insert into [dbo].[Stu]([Name],[subject],[credit]) values('张三','数学',100),('张三','英语',100)
查询结果,如下图Stu_Sum表中
select * from [Stu]
select * from [Stu_sum]
DELETE触发器
先插入几条值
insert into [dbo].[Stu]([Name],[subject],[credit]) values('张三','体育',60),('李四','英语',100),('李四','语文',80)
先查询一下表看一下表目前的值
目标删除张三的体育成绩,同时更新张三的总成绩。
DELETE触发器创建
CREATE TRIGGER dbo.stu_TRIGGER_DELETEON dbo.StuAFTER DELETE
AS
BEGINSET NOCOUNT ON;--不返回影响行数--删除数据时会产生DELETEED表(表结构和Stu一样)--把[Stu_sum]存在的数据更新掉update b set b.number=isnull(b.number,0)-a.credit from (select [Name],sum(credit) credit from DELETED group by [Name]) ajoin [dbo].[Stu_sum] b on a.Name=b.Name
END
GO
执行删除语句
delete from [dbo].[Stu] where Name='张三' and subject='体育'
UPDATE触发器
案例:需要把张三数学改成98分,同时修改张三总成绩
创建UPDATE触发器
CREATE TRIGGER dbo.stu_TRIGGER_UPDATEON dbo.StuAFTER UPDATE
AS
BEGINSET NOCOUNT ON;--不返回影响行数--更新数据时会产生INSERTED 、DELETEED表(表结构和Stu一样)--把[Stu_sum]存在的数据更新掉update b set b.number=isnull(b.number,0)+d.credit from (select Name,sum(credit) credit from(select a.[Name],b.credit-a.credit credit from DELETED a join INSERTED b on a.ID=b.ID) c group by [name]) djoin [dbo].[Stu_sum] b on d.Name=b.Name
END
GO
执行语句
update [dbo].[Stu] set [credit]=98 where [Name]='张三' and [subject]='数学'
替代触发器
与前面介绍了三种AFTER触发器不同,SQLServer 服务器在执行触发AFTER触发器的SQL代码后,先建立临时的INSERTED和DELETED表,然后执行SQL代码中对数据的操作,最后才激活触发器中的代码。而对于替代(INSTEAD OF)触发器,SQL SERVER服务器执行触发INSERTAD OF 触发器时,先建立临时的INSERTED和DELETED表,然后直接触发INSTEAD OF 触发器。
案例:当用户插入学生单科成绩大于100分时,直接提示报错。
创建触发器
CREATE TRIGGER dbo.stu_TRIGGER_INSTEADOFON dbo.StuINstead of INSERT
AS
BEGINif exists(select ID from inserted where credit>100)beginselect '插入成绩错误' as 错误原因end
END
GO
执行插入语句
insert into [dbo].[Stu]([Name],[subject],[credit]) values('张三','体育',130)
执行结果,错误的成绩没有被插入。