SQL Server的存储过程与触发器以及系统函数和自定义函数


Posted in SQL Server onApril 10, 2022

一、函数

函数分为(1)系统函数,(2)自定义函数。

其中自定义函数又可以分为(1)标量值函数(返回单个值),(2)表值函数(返回查询结果)

本文主要介绍自定义函数的使用。

(1)编写一个函数求该银行的金额总和

create function GetSumCardMoney()
returns money 
as
begin
	declare @AllMOney money
	select @AllMOney = (select SUM(CardMoney) from BankCard)
	return @AllMOney
end

函数调用

select dbo.GetSumCardMoney()

上述函数没有参数,下面介绍有参数的函数的定义及使用

(2)传入账户编号,返回账户真实姓名

create function GetNameById(@AccountId int)
returns  varchar(20)
as
begin
	declare @RealName varchar(20)
	select @RealName = (select RealName from AccountInfo where AccountId = @AccountId)
	return @RealName
end

函数调用

print dbo.GetNameById(2)

(3)传递开始时间和结束时间,返回交易记录(存钱取钱),交易记录中包含 真实姓名,卡号,存钱金额,取钱金额,交易时间。

方案一(逻辑复杂,函数内容除了返回结果的sql语句还有其他内容,例如定义变量等):

create function GetExchangeByTime(@StartTime varchar(30),@EndTime varchar(30))
returns @ExchangeTable table
(
	RealName varchar(30),  --真实姓名
	CardNo varchar(30),    --卡号
	MoneyInBank money,     --存钱金额
	MoneyOutBank money,    --取钱金额
	ExchangeTime smalldatetime  --交易时间
)
as
begin
	insert into @ExchangeTable
	select AccountInfo.RealName,CardExchange.CardNo,CardExchange.MoneyInBank,
	CardExchange.MoneyOutBank,CardExchange.ExchangeTime from CardExchange
	left join BankCard on CardExchange.CardNo = BankCard.CardNo
	left join AccountInfo on BankCard.AccountId = AccountInfo.AccountId
	where CardExchange.ExchangeTime between @StartTime+' 00:00:00' and @EndTime+' 23:59:59'
	return
end

函数调用

select * from GetExchangeByTime('2018-6-1','2018-7-1')

方案二(逻辑简单,函数内容直接是一条sql查询语句):

create function GetExchangeByTime(@StartTime varchar(30),@EndTime varchar(30))
returns table
as
	return
	select AccountInfo.RealName,CardExchange.CardNo,CardExchange.MoneyInBank,
	CardExchange.MoneyOutBank,CardExchange.ExchangeTime from CardExchange
	left join BankCard on CardExchange.CardNo = BankCard.CardNo
	left join AccountInfo on BankCard.AccountId = AccountInfo.AccountId
	where CardExchange.ExchangeTime between @StartTime+' 00:00:00' and @EndTime+' 23:59:59'
go

函数调用:

select * from GetExchangeByTime('2018-6-19','2018-6-19')

(4)查询银行卡信息,将银行卡状态1,2,3,4分别转换为汉字“正常,挂失,冻结,注销”,根据银行卡余额显示银行卡等级 30万以下为“普通用户”,30万及以上为"VIP用户",分别显示卡号,身份证,姓名,余额,用户等级,银行卡状态。

方案一:直接在sql语句中使用case when

select * from AccountInfo
select * from BankCard
select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额,
case
	when CardMoney < 300000 then '普通用户'
	else 'VIP用户' 
end 用户等级,
case
	when CardState = 1 then '正常'
	when CardState = 2 then '挂失'
	when CardState = 3 then '冻结'
	when CardState = 4 then '注销'
	else '异常'
end 卡状态
from BankCard inner join AccountInfo on BankCard.AccountId = AccountInfo.AccountId

方案二:将等级和状态用函数实现

create function GetGradeByMoney(@myMoney int)
returns varchar(10)
as
begin
	declare @result varchar(10)
	if @myMoney < 3000 
		set @result = '普通用户'
	else
		set @result = 'VIP用户'
	return @result
end
go

create function GetStatusByNumber(@myNum int)
returns varchar(10)
as
begin
	declare @result varchar(10)
	if @myNum = 1
		set @result = '正常'
	else if @myNum = 2
		set @result = '挂失'
	else if @myNum = 3
		set @result = '冻结'
	else if @myNum = 4
		set @result = '注销'
	else
		set @result = '异常'	
	return @result
end
go

函数调用实现查询功能

select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额,
dbo.GetGradeByMoney(CardMoney) 账户等级,dbo.GetStatusByNumber(CardState) 卡状态
from BankCard inner join AccountInfo on BankCard.AccountId = AccountInfo.AccountId

(5)编写函数,根据出生日期求年龄,年龄求实岁,例如:

​ 生日为2000-5-5,当前为2018-5-4,年龄为17岁
​ 生日为2000-5-5,当前为2018-5-6,年龄为18岁

测试数据如下:

create table Emp
(
	EmpId int primary key identity(1,2), --自动编号
	empName varchar(20), --姓名
	empSex varchar(4),   --性别
	empBirth smalldatetime --生日
)
insert into Emp(empName,empSex,empBirth) values('刘备','男','2008-5-8')
insert into Emp(empName,empSex,empBirth) values('关羽','男','1998-10-10')
insert into Emp(empName,empSex,empBirth) values('张飞','男','1999-7-5')
insert into Emp(empName,empSex,empBirth) values('赵云','男','2003-12-12')
insert into Emp(empName,empSex,empBirth) values('马超','男','2003-1-5')
insert into Emp(empName,empSex,empBirth) values('黄忠','男','1988-8-4')
insert into Emp(empName,empSex,empBirth) values('魏延','男','1998-5-2')
insert into Emp(empName,empSex,empBirth) values('简雍','男','1992-2-20')
insert into Emp(empName,empSex,empBirth) values('诸葛亮','男','1993-3-1')
insert into Emp(empName,empSex,empBirth) values('徐庶','男','1994-8-5')

函数定义:

create function GetAgeByBirth(@birth smalldatetime)
returns int
as
begin
	declare @age int
	set @age = year(getdate()) - year(@birth)
	if month(getdate()) < month(@birth)
		set @age = @age - 1
	if month(getdate()) = month(@birth) and day(getdate()) < day(@birth)
		set @age = @age -1
	return @age
end

函数调用实现查询

select *,dbo.GetAgeByBirth(empBirth) 年龄 from Emp

二、触发器

触发器分类:(1) “Instead of”触发器(2)“After”触发器
“Instead of”触发器:在执行操作之前被执行
“After”触发器:在执行操作之后被执行

触发器中后面的案例中需要用到的表及测试数据如下:

--部门
create table Department
(
	DepartmentId varchar(10) primary key , --主键,自动增长
	DepartmentName nvarchar(50), --部门名称
)
--人员信息
create table People
(
	PeopleId int primary key identity(1,1), --主键,自动增长
	DepartmentId varchar(10), --部门编号,外键,与部门表关联
	PeopleName nvarchar(20), --人员姓名
	PeopleSex nvarchar(2), --人员性别
	PeoplePhone nvarchar(20), --电话,联系方式
)
insert into Department(DepartmentId,DepartmentName)
values('001','总经办')
insert into Department(DepartmentId,DepartmentName)
values('002','市场部')
insert into Department(DepartmentId,DepartmentName)
values('003','人事部')
insert into Department(DepartmentId,DepartmentName)
values('004','财务部')
insert into Department(DepartmentId,DepartmentName)
values('005','软件部')
insert into People(DepartmentId,PeopleName,PeopleSex,PeoplePhone)
values('001','刘备','男','13558785478')
insert into People(DepartmentId,PeopleName,PeopleSex,PeoplePhone)
values('001','关羽','男','13558788785')
insert into People(DepartmentId,PeopleName,PeopleSex,PeoplePhone)
values('002','张飞','男','13698547125')

(1)假设有部门表和员工表,在添加员工的时候,该员工的部门编号如果在部门表中找不到,则自动添加部门信息,部门名称为"新部门"。

编写触发器:

create trigger tri_InsertPeople on People
after insert
as
if not exists(select * from Department where DepartmentId = (select DepartmentId from inserted))
	insert into Department(DepartmentId,DepartmentName)
	values((select DepartmentId from inserted),'新部门')
go

测试触发器:

insert People(DepartmentId,PeopleName,PeopleSex,PeoplePhone)
values('009','赵云','男','13854587456')

我们会发现,当插入赵云这个员工的时候会自动向部门表中添加数据。

(2)触发器实现,删除一个部门的时候将部门下所有员工全部删除。

编写触发器:

create trigger tri_DeleteDept on Department
after delete
as
delete from People where People.DepartmentId = 
(select DepartmentId from deleted)
go

测试触发器:

delete Department where DepartmentId = '001'

我们会发现当我们删除此部门的时候,同时会删除该部门下的所有员工

(3)创建一个触发器,删除一个部门的时候判断该部门下是否有员工,有则不删除,没有则删除。

编写触发器:

drop trigger tri_DeleteDept	--删除掉之前的触发器,因为当前触发器也叫这个名字
create trigger tri_DeleteDept on Department
Instead of delete
as
  if not exists(select * from People where DepartmentId = (select DepartmentId from deleted))
  begin
	delete from Department where DepartmentId = (select DepartmentId from deleted)
  end
go

测试触发器:

delete Department where DepartmentId = '001'
delete Department where DepartmentId = '002'
delete Department where DepartmentId = '003'

我们会发现,当部门下没有员工的部门信息可以成功删除,而部门下有员工的部门并没有被删除。

(4)修改一个部门编号之后,将该部门下所有员工的部门编号同步进行修改

编写触发器:

create trigger tri_UpdateDept on Department
after update
as
	update People set DepartmentId = (select DepartmentId from inserted)
	where DepartmentId = (select DepartmentId from deleted)
go

测试触发器:

update Department set DepartmentId = 'zjb001' where DepartmentId='001'

我们会发现不但部门信息表中的部门编号进行了修改,员工信息表中部门编号为001的信息也被一起修改了。

三、存储过程

存储过程(Procedure)是SQL语句和流程控制语句的预编译集合。

(1)没有输入参数,没有输出参数的存储过程。

定义存储过程实现查询出账户余额最低的银行卡账户信息,显示银行卡号,姓名,账户余额

--方案一
create proc proc_MinMoneyCard
as
    select top 1 CardNo 银行卡号,RealName 姓名,CardMoney 余额
    from BankCard inner join AccountInfo on BankCard.AccountId = AccountInfo.AccountId
    order by CardMoney asc
go

--方案二:(余额最低,有多个人则显示结果是多个)
create proc proc_MinMoneyCard
as
    select CardNo 银行卡号,RealName 姓名,CardMoney 余额
    from BankCard inner join AccountInfo on BankCard.AccountId = AccountInfo.AccountId
    where CardMoney=(select MIN(CardMoney) from BankCard)
go

执行存储过程:

exec proc_MinMoneyCard

(2)有输入参数,没有输出参数的存储过程

模拟银行卡存钱操作,传入银行卡号,存钱金额,实现存钱操作

create proc proc_CunQian
@CardNo varchar(30),
@MoneyInBank money
as
    update BankCard set CardMoney = CardMoney + @MoneyInBank where CardNo = @CardNo
    insert into CardExchange(CardNo,MoneyInBank,MoneyOutBank,ExchangeTime)
    values(@CardNo,@MoneyInBank,0,GETDATE())
--go

执行存储过程:

exec proc_CunQian '6225125478544587',3000

(3)有输入参数,没有输出参数,但是有返回值的存储过程(返回值必须整数)。

模拟银行卡取钱操作,传入银行卡号,取钱金额,实现取钱操作,取钱成功,返回1,取钱失败返回-1

create proc proc_QuQian
@CardNo varchar(30),
@MoneyOutBank money
as
    update BankCard set CardMoney = CardMoney - @MoneyOutBank where CardNo = @CardNo
    if @@ERROR <> 0
        return -1
    insert into CardExchange(CardNo,MoneyInBank,MoneyOutBank,ExchangeTime)
    values(@CardNo,0,@MoneyOutBank,GETDATE())
    return 1
go

执行存储过程:

declare @returnValue int
exec @returnValue = proc_QuQian '662018092100000002',1000000
print @returnValue

(4)有输入参数,有输出参数的存储过程

查询出某时间段的银行存取款信息以及存款总金额,取款总金额,传入开始时间,结束时间,显示存取款交易信息的同时,返回存款总金额,取款总金额。

create proc proc_SelectExchange
    @startTime varchar(20),  --开始时间
    @endTime varchar(20),    --结束时间
    @SumIn money output,     --存款总金额
    @SumOut money output    --取款总金额
as
select @SumIn = (select SUM(MoneyInBank) from CardExchange 
				where ExchangeTime between @startTime+' 00:00:00' and @endTime+' 23:59:59')
select @SumOut = (select SUM(MoneyOutBank) from CardExchange 
				where ExchangeTime between @startTime+' 00:00:00' and @endTime+' 23:59:59')
select * from CardExchange 
where ExchangeTime between @startTime+' 00:00:00' and @endTime+' 23:59:59'
go

执行存储过程:

declare @SumIn money     --存款总金额
declare @SumOut money   --取款总金额
exec proc_SelectExchange '2018-1-1','2018-12-31',@SumIn output,@SumOut output
select @SumIn
select @SumOut

(5)具有同时输入输出参数的存储过程

密码升级,传入用户名和密码,如果用户名密码正确,并且密码长度<8,自动升级成8位密码

--有输入输出参数(密码作为输入参数也作为输出参数)
--密码升级,传入用户名和密码,如果用户名密码正确,并且密码长度<8,自动升级成8位密码
select FLOOR(RAND()*10) --0-9之间随机数
create proc procPwdUpgrade
@cardno nvarchar(20),
@pwd nvarchar(20) output
as
	if not exists(select * from BankCard where CardNo=@cardno and CardPwd=@pwd)
		set @pwd = ''
	else
	begin
		if len(@pwd) < 8
		begin
			declare @len int = 8- len(@pwd)
			declare @i int = 1
			while @i <= @len
			begin
				
				set @pwd = @pwd + cast(FLOOR(RAND()*10) as varchar(1))
				set @i = @i+1
			end
			update BankCard set CardPwd = @pwd where CardNo=@cardno
		end
	end
go
declare @pwd nvarchar(20) = '123456'
exec procPwdUpgrade '6225547854125656',@pwd output
select @pwd

到此这篇关于SQL Server中函数、存储过程与触发器的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

SQL Server 相关文章推荐
SQL Server基本使用和简单的CRUD操作
Apr 05 SQL Server
SQL SERVER中常用日期函数的具体使用
Apr 08 SQL Server
sqlserver2017共享功能目录路径不可改的解决方法
Apr 16 SQL Server
SQLServer 错误: 15404,无法获取有关 Windows NT 组/用户 WIN-8IVSNAQS8T7\Administrator 的信息
Jun 30 SQL Server
SQL Server代理:理解SQL代理错误日志处理方法
Jun 30 SQL Server
SQL Server中使用判断语句(IF ELSE/CASE WHEN )案例
Jul 07 SQL Server
SQL SERVER实现连接与合并查询
Feb 24 SQL Server
SQL Server数据库基本概念、组成、常用对象与约束
Mar 20 SQL Server
使用 MybatisPlus 连接 SqlServer 数据库解决 OFFSET 分页问题
Apr 22 SQL Server
SQL Server 忘记密码以及重新添加新账号
Apr 26 SQL Server
SQL Server数据库备份和恢复数据库的全过程
Jun 14 SQL Server
在SQL Server中使用 Try Catch 处理异常的示例详解
Jul 15 SQL Server
SQL Server表分区降低运维和维护成本
SQL Server使用导出向导功能
SQL Server内存机制浅探
sqlserver连接错误之SQL评估期已过的问题解决
SQL Server远程连接的设置步骤(图文)
SQLServer RANK() 排名函数的使用
SQL CASE 表达式的具体使用
Mar 21 #SQL Server
You might like
如何开始收听短波广播
2021/03/01 无线电
PHP 中的类
2006/10/09 PHP
phpinfo 系统查看参数函数代码
2009/06/05 PHP
php对象和数组相互转换的方法
2015/05/12 PHP
Mootools 1.2教程 函数
2009/09/15 Javascript
从jQuery.camelCase()学习string.replace() 函数学习
2011/09/13 Javascript
用jquery统计子菜单的条数示例代码
2013/10/18 Javascript
推荐5 个常用的JavaScript调试技巧
2015/01/08 Javascript
Google 地图获取API Key详细教程
2016/08/06 Javascript
微信小程序使用picker实现时间和日期选择框功能【附源码下载】
2017/12/11 Javascript
Vue shopCart 组件开发详解
2018/01/26 Javascript
angularJS实现动态添加,删除div方法
2018/02/27 Javascript
vue-socket.io跨域问题有效解决方法
2020/02/11 Javascript
微信小程序订阅消息(java后端实现)开发
2020/06/01 Javascript
浅谈vue中document.getElementById()拿到的是原值的问题
2020/07/26 Javascript
[04:04]显微镜下的DOTA2第六期——电影级别的华丽团战
2014/06/20 DOTA
[01:03:36]Ti4 循环赛第三日DK vs Titan
2014/07/12 DOTA
使用PYTHON创建XML文档
2012/03/01 Python
Python基于checksum计算文件是否相同的方法
2015/07/09 Python
Python算法输出1-9数组形成的结果为100的所有运算式
2017/11/03 Python
TensorFlow深度学习之卷积神经网络CNN
2018/03/09 Python
python+opencv实现霍夫变换检测直线
2020/10/23 Python
Python中@property的理解和使用示例
2019/06/11 Python
对Python 简单串口收发GUI界面的实例详解
2019/06/12 Python
HTML5实现自带进度条和滑块滑杆效果
2018/04/17 HTML / CSS
介绍java中初始化块的使用
2012/09/11 面试题
介绍一下linux的文件系统
2015/10/06 面试题
受欢迎的大学生自我评价
2013/12/05 职场文书
优秀幼教自荐信
2014/02/03 职场文书
幼儿园家长寄语
2014/04/02 职场文书
学习优秀共产党员先进事迹思想报告
2014/09/17 职场文书
党员批评与自我批评总结
2014/10/15 职场文书
大学生毕业个人总结
2015/02/15 职场文书
2016年校园重阳节广播稿
2015/12/18 职场文书
如何理解及使用Python闭包
2021/06/01 Python
详解Python中下划线的5种含义
2021/07/15 Python