添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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