SQL Server:存储过程-可以被其他语言调用的函数
存储过程时数据库的一个重要对象,是 SQL 语句和流程控制语句的预编译集合。和数据库表是一个层级的。数据库对象还有索引、视图、收发器。
可以封装 SQL 语句,可以用来完成一些较为复杂的业务逻辑,并且可以入参出参(类似于 Java 中的方法的书写)。Java 能做的事,存储过程也能做。
有人会说,少用 SQL 来处理业务逻辑,因为 SQL 的专长在于检索查询,而不在复杂的业务逻辑判断和处理上。
一、存储过程和函数的区别
函数经常被嵌入到 SQL 语句中调用。
存储过程可以直接被外部程序调用,例如 c# 或者 Java。
二、存储过程的优点
在生产环境下,可以通过直接修改存储过程的方式修改业务逻辑或 bug,而不用重启服务器
执行速度块,存储过程经过编译之后,回避单独一条一条执行要快。
减少网络传输流量。
方便优化。
三、存储过程的缺点
过程化变成,复杂业务处理的维护成本高。
调试不方便
不同数据库之间可移植性差。因为不同数据库语法不一样。
四、存储过程的基本语法
一般再存储过程中写事务。
用 as 表示代码开始,go 表示代码结束。
Create proc 存储过程的名字
go
五、 存储过程的例子
(1) 没有输入参数,没有输出参数的存储过程
定义存储过程,实现查询出账户余额最低的银行卡账户信息,显示银行卡号、姓名、余额
银行卡号、余额在银行表,姓名在账户表,所以是多表查询
法一:
通过order 排序,结合提取 top1 来获取最小值
Create proc proc_MinMoneyCard
Select top 1 CardNo, RealName, CardMoney from BankCard
Inner join AccountInfo on BankCard.AccoundID = AccountInfo.AccountId
Order by CardMoney
go
下一次再要查余额最低的银行卡账户信息,就不用再写一遍查询的代码了,直接调用存储过程即可
Exec proc_MinMoneyCard
上面的写法有点问题:假设有多个人余额最低,上面的命令就有问题了。
法二(余额最低,多个人并列,都可以查出来):
筛选的条件改成,Card Money = 最低值
Drop proc proc_MinMoneyCard
Create proc proc_MinMoneyCard
Select CardNo, RealName, CardMoney from BankCard
Inner join AccountInfo on BankCard.AccountId = AccountInfo.AccountId
Where CardMoney =
(select min(CardMoney) from BankCard)
Exce proc_MinMoneyCard
(2) 有输入参数,没有输出参数的存储过程
模拟银行卡存钱操作,传入银行卡号,存钱金额,实现存钱操作
将余额钱变多,并产生一条交易记录
两个参数,需要写在 as 的前面
Create proc proc_Cunqian
@CardNo varchar(30)
@money money
Update BankCard set CardMoney = CardMoney + @money
Where CardNo = @CardNo
Insert into CardExchange(CardNo, MoneyInBank, MoneyOutBank, Exchange Teim)
Values(@CardNo, @money, 0, getdate())
go
未来再有人存钱,就不需要写代码,只要调用存储过程就行
Exec proc_Cunqian '6225547858741263', 1000
(3) 有输入参数,没有输出参数,但是有返回值的存储过程(返回值必须整数)
模拟银行卡取钱操作,传入银行卡号,取钱金额,实现取钱操作
取钱成功,返回1,取钱失败返回 -1
返回值只能返回整数
@@ERROR 可以取出最后执行的一条 SQL 语句的错误代码。如果最后执行的 SQL 语句没有错误,则它的值等于 0
Create proc proc_Quqian
@CardNo carchar(30)
@money money
-- 进行修改余额的操作
Update BankCard set CardMoney = CardMoney - @money
Where CardNo = @CardNo
-- 如果有错误,则返回-1
-- 在存储过程中,return 后面的所有代码都不会执行
If @@ERROR <>0
Return -1
Insert into CardExchange(CardNo, MoneyInBank, MoneyOutBank, ExchangeTime)
Values(@CardNo, 0, @money, getdate())
Return 1
Go
定义一个变量来接收返回值
Declare @returnValue int
Exec @returnValue = proc_Quqian '6225125478544587', 2000
Select @returnValue
(4) 有输入参数,有输出参数的存储过程
输出参数具备输出功能,可以把值带到外面,有点类似于存储过程返回值
返回值必须是整数,且只能返回一个值。输出参数作为返回结果的方式,可以返回多个输出参数,并且对于数据类型没有限制。
查询出某时间段的银行存取款信息以及存款总金额,取款总金额
传入开始时间、结束时间,显示存取款交易信息的同时,返回存款总金额,取款总金额。
Create proc proc_selectExChange
@start varchar(20),--开始时间
@end varchar(20),--结束时间
-- 定义输出参数,要在参数名后面加上空格output
@sumIn Money output,-- 存款总金额
@sumOut money output, --取款总金额
Select @sumIn = (select sum(MoneyInBank) from CardExchange where ExchangeTime between @start + '00:00:00' and @end + '23:59:59')
Select @sumOut = (select sum(MoneyOutBank) from CardExchange where ExchangeTime between @start + '00:00:00' and @end + '23:59:59')
Select * from CardExchange
Where ExchangeTime between @start + '00:00:00' and @end + '23:59:59'
Go
输出参数的位置不能放常量,需要先定义变量,然后放在输出参数的位置,并且要在输出参数后面加上 output 关键字。
Declare @sumIn money
Declare @sumOut money
Exec proc_selectExChange '2020-1-1'. '2020-12-11', @sumIn output, @sumOut output
Select @sumIn
Select @ sumOut
(5)具有同时输入输出参数的存储过程
第4个例子的四个参数功能是非常单一的。输入的参数只能输入,输出的参数只能输出。
下面要做的是,参数本身作为输入,在存储过程里把这个参数的值改了,它还要作为输出。
一个参数同时具备输入输出功能。
实现一个密码升级功能:传入卡号和密码,如果卡号密码正确,并且密码长度小于8,自动升级成8为密码。如果密码长度大于8,则不修改。
两个参数,一个参数是卡号,只有输入功能;另一个参数是密码,既有输入功能,也有输出功能。
Select * from BankCard
--floor 是取整;rank是生成(0,1)的小数
Select floor(rand()*10)
Create proc procPwdUpgrade
@CardNo nvarchar(20), -- 卡号
@pwd nvarchar(20) output, --密码。在 SQL Server 里面,既能输入、也能输出的参数,也是在参数后面加上 output 关键字就行
-- 存在的判断用 exists 命令
If not exists (select * from BankCard where CardNo = @CardNo and CardPwd = @pwd)
-- 如果不存在卡号,则密码输出空值
Set @pwd = ''
Begin
-- 如果密码小于8位,就升级成8位
If len(@pwd) < 8
Begin
Declare @len int = 8 - len(@pwd)
Declare @i int = 1
While @i <= @len
Begin
--cast(xx as varchar(1)) 是把整数型的变量转换成字符串型的变量。
Set @pwd = @pwd + cast(floor(rand()*10) as varchar(1))
Set @i = @i+1