在SQL Server中使用 Try Catch 处理异常的示例详解


Posted in SQL Server onJuly 15, 2022

如何在 SQL Server 中使用 Try Catch 处理错误?

从 SQL Server 2005 开始,我们在TRY 和 CATCH块的帮助下提供了结构错误处理机制。使用TRY-CATCH的语法如下所示。

BEGIN TRY
--这里写可能导致错误的语句
END TRY
BEGIN CATCH
--当错误发生后,这里的语句将会被执行
END CATCH
-- 其他语句

有可能抛出异常的SQL 语句需要放在BEGIN TRY和END TRY块之间。如果在 TRY 块中发生异常,则控制权立即转移到相应的CATCH块。如果 TRY 块中没有发生异常,则直接跳过CATCH块,执行CATCH块之后的语句。

注意:被 CATCH 捕获的错误不会返回给调用应用程序。如果要将错误信息返回给调用应用程序,则需要将 RAISERROR() 函数显式与 catch 块一起使用。在之前的文章中,我们讨论了如何使用 RAISERROR() 函数显式地引发错误。

示例:了解 SQL Server 中的 Try-Catch 实现。

在下面的示例中,我们使用 SQL Server TRY CATCH 实现和用户定义的错误语句来创建一个用于除以 2 个变量值的存储过程。

IF OBJECT_ID('spDivideTwoNumbers','P') IS NOT NULL
    DROP PROCEDURE spDivideTwoNumbers
GO
CREATE PROCEDURE spDivideTwoNumbers(
@Number1 INT, 
@Number2 INT
)
AS
BEGIN
  DECLARE @Result INT
  SET @Result = 0
  BEGIN TRY
    SET @Result = @Number1 / @Number2
    PRINT '结果是: ' + CAST(@Result AS VARCHAR)
  END TRY
  BEGIN CATCH
    PRINT '第二个数字不能为0'
  END CATCH
END

让我们来测试一下这个存储过程

exec spDivideTwoNumbers 100,1

输出: 结果是: 100

exec spDivideTwoNumbers 100,0

输出: 第二个数字不能为0

当我们用正确的值执行上述存储过程时,程序就不会出现错误。这意味着在执行完 try 块中的所有语句后,控件直接跳转到存在于 catch 块之后的语句,而不执行 catch 块。

如果在执行过程中,即在try块中发生任何错误,那么在这种情况下,从发生错误的行开始,控制权直接跳转到catch块。所以 try 块中的其余语句将不会执行,而 catch 块将执行。

注意:在上面的程序中,当错误发生时,我们会显示一条用户自定义的错误消息“第二个数字不能为0”。然而,我们还可以通过调用函数 Error_Message 来显示原始错误消息。为了测试这个重写catch块内的代码如下

ALTER PROCEDURE spDivideTwoNumbers(
@Number1 INT, 
@Number2 INT
)
AS
BEGIN
  DECLARE @Result INT
  SET @Result = 0
  BEGIN TRY
    SET @Result = @Number1 / @Number2
    PRINT '结果是: ' + CAST(@Result AS VARCHAR)
  END TRY
  BEGIN CATCH
    PRINT ERROR_MESSAGE()
  END CATCH
END

再次执行: exec spDivideTwoNumbers 100,0
输出: 遇到以零作除数错误。

ERROR_MESSAGE 是系统函数, 此函数返回导致 TRY...CATCH 构造的 CATCH 块执行的错误消息文本。

在 SQL Server 中使用 try-catch 的示例

我们将使用下面的 Product 和 ProductSales 表来了解如何使用 RaiseError 和 @ERROR 系统定义函数来处理 SQL Server 中的错误。

Product (产品表)

ProductID Name Price Quantity
101 Laptop 15000 100
102 Desktop 20000 150
104 Mobile 3000 200
105 Tablet 4000 250

ProductSales (产品销售表)

ProductSalesID ProductID QuantitySold
1 101 10
2 102 15
3 104 30
4 105 35

请使用以下 SQL 脚本创建并使用示例数据填充 Product 和 ProductSales 表。

IF OBJECT_ID('dbo.Product','U') IS NOT NULL
    DROP TABLE dbo.Product
IF OBJECT_ID('dbo.ProductSales','U') IS NOT NULL
    DROP TABLE dbo.ProductSales
GO
CREATE TABLE Product
(
  ProductID INT PRIMARY KEY, 
  Name VARCHAR(40), 
  Price INT,
  Quantity INT
 )
GO
INSERT INTO Product VALUES(101, 'Laptop', 15000, 100)
INSERT INTO Product VALUES(102, 'Desktop', 20000, 150)
INSERT INTO Product VALUES(103, 'Mobile', 3000, 200)
INSERT INTO Product VALUES(104, 'Tablet', 4000, 250)
GO
CREATE TABLE ProductSales
(
  ProductSalesId INT PRIMARY KEY,
  ProductId INT,
  QuantitySold INT
) 
GO
INSERT INTO ProductSales VALUES(1, 101, 10)
INSERT INTO ProductSales VALUES(2, 102, 15)
INSERT INTO ProductSales VALUES(3, 103, 30)
INSERT INTO ProductSales VALUES(4, 104, 35)
GO

在 SQL Server 中使用 TRY Catch 实现产品销售的存储过程

CREATE PROCEDURE spSellProduct
@ProductID INT,
@QuantityToSell INT
AS
BEGIN

  -- 首先我们需要检查待销售产品的可用库存
  DECLARE @StockAvailable INT
  SELECT @StockAvailable = QuantityAvailable FROM Product WHERE ProductId = @ProductId

    --如果可用库存小于要销售的数量,抛出错误
  IF(@StockAvailable< @QuantityToSell)
  BEGIN
    Raiserror('可用库存不足',16,1)
  END
  
  -- 如果可用库存充足
  ELSE
  BEGIN
    BEGIN TRY
      -- 我们需要开启一个事务
      BEGIN TRANSACTION

      -- 首先做减库存操作
      UPDATE Product SET Quantity = (Quantity - @QuantityToSell) WHERE ProductID = @ProductID

      -- 计算当前最大的产品销售ID,即 MaxProductSalesId
      DECLARE @MaxProductSalesId INT
      SELECT @MaxProductSalesId = CASE 
          WHEN  MAX(ProductSalesId) IS NULL THEN 0 
          ELSE MAX(ProductSalesId) 
          END 
      FROM ProductSales

      -- 把 @MaxProductSalesId 加一, 所以我们会避免主键冲突 
      --(解释下,建表的时候,没有设置主键自增,所以需要人工处理自增)
      Set @MaxProductSalesId = @MaxProductSalesId + 1

      -- 把销售的产品数量记录到ProductSales表中
      INSERT INTO ProductSales VALUES (@MaxProductSalesId, @ProductId, @QuantityToSell)

      -- 最后,提交事务
      COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
      -- 如果发生了异常,回滚事务
      ROLLBACK TRANSACTION
      -- 输出错误详情
      SELECT ERROR_NUMBER() as ErrorNumber,
          ERROR_MESSAGE() as ErrorMessage,
          ERROR_PROCEDURE() as ErrorProcedure,
          ERROR_STATE() as ErrorState,
          ERROR_SEVERITY() as ErrorSeverity,
          ERROR_LINE() as ErrorLine
    END CATCH
  End
END

在存储过程 spSellProduct 中,Begin Transaction 和 Commit Transaction 语句被包装在 Begin Try 和 End Try 块之间。如果包含在 BEGIN TRY 和 END TRY 块中的代码没有发生错误,则执行 COMMIT TRANSACTION 语句并将更改永久保存到数据库。

如果在 try 块中发生错误,则立即跳转到 CATCH 块,并且在 CATCH 块中,我们正在回滚事务。因此,使用 Try/Catch 构造处理错误比使用 SQL Server 中的 @@Error 系统函数要容易得多。

SQL Server 还提供了一些我们可以在 CATCH 块范围内使用的内置函数,这些函数用于检索有关发生的错误的更多信息,如果这些函数在 CATCH 块范围之外执行,它们将返回 NULL。

注意:我们不能在用户定义的函数中使用 TRY/CATCH

到此这篇关于在 SQL Server 中使用 Try Catch 处理异常的文章就介绍到这了,更多相关SQL Server 使用 Try Catch 处理异常内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

SQL Server 相关文章推荐
SQL Server数据定义——模式与基本表操作
Apr 05 SQL Server
SQLServer 日期函数大全(小结)
Apr 08 SQL Server
解决sql server 数据库,sa用户被锁定的问题
Jun 11 SQL Server
sql字段解析器的实现示例
Jun 23 SQL Server
SQL写法--行行比较
Aug 23 SQL Server
万能密码的SQL注入漏洞其PHP环境搭建及防御手段
Sep 04 SQL Server
Windows环境下实现批量执行Sql文件
Oct 05 SQL Server
SQL Server实现分页方法介绍
Mar 16 SQL Server
SQL Server表分区降低运维和维护成本
Apr 08 SQL Server
SQL Server数据库查询出现阻塞之性能调优
Apr 10 SQL Server
SQL SERVER中的流程控制语句
May 25 SQL Server
SQL Server2019安装的详细步骤实战记录(亲测可用)
Jun 10 SQL Server
SQL中的连接查询详解
Jun 21 #SQL Server
SQL Server数据库备份和恢复数据库的全过程
一次SQL查询优化原理分析(900W+数据从17s到300ms)
Jun 10 #SQL Server
SQL Server2019安装的详细步骤实战记录(亲测可用)
SQL Server携程核心系统无感迁移到MySQL实战
SQL解决未能删除约束问题drop constraint
May 30 #SQL Server
SQL使用复合索引实现数据库查询的优化
May 25 #SQL Server
You might like
php将数据库中所有内容生成静态html文档的代码
2010/04/12 PHP
PHP代码审核的详细介绍
2013/06/13 PHP
Php无限级栏目分类读取的实现代码
2014/02/19 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
javascript中的关于类型转换的性能优化
2010/12/14 Javascript
js bind 函数 使用闭包保存执行上下文
2011/12/26 Javascript
关于onchange事件在IE和FF下的表现及解决方法
2014/03/08 Javascript
JQuery中DOM事件冒泡实例分析
2015/06/13 Javascript
jQuery实现带延迟的二级tab切换下拉列表效果
2015/09/01 Javascript
JQuery菜单效果的两个实例讲解(3)
2015/09/17 Javascript
jQuery中serializeArray()与serialize()的区别实例分析
2015/12/09 Javascript
javascript结合Flexbox简单实现滑动拼图游戏
2016/02/18 Javascript
JS实现间歇滚动的运动效果实例
2016/12/22 Javascript
jQuery中select与datalist制作下拉菜单时的区别浅析
2016/12/30 Javascript
详解Vue监听数据变化原理
2017/03/08 Javascript
vue.js项目打包上线的图文教程
2017/11/16 Javascript
jQuery实现鼠标滑过商品小图片上显示对应大图片功能【测试可用】
2018/04/27 jQuery
JS实现简单随机3D骰子
2019/10/24 Javascript
vue 监听 Treeselect 选择项的改变操作
2020/08/31 Javascript
[06:44]2014DOTA2国际邀请赛-钥匙体育馆开战 开幕式振奋人心
2014/07/19 DOTA
Python实现发送email的几种常用方法
2014/08/18 Python
python+selenium+autoit实现文件上传功能
2017/08/23 Python
Django框架多表查询实例分析
2018/07/04 Python
python使用循环打印所有三位数水仙花数的实例
2018/11/13 Python
在Python中调用Ping命令,批量IP的方法
2019/01/26 Python
django实现HttpResponse返回json数据为中文
2020/03/27 Python
css3选择器基本介绍
2014/12/15 HTML / CSS
俄罗斯皮肤健康中心:Pharmacosmetica.ru
2020/02/22 全球购物
建筑专业自荐信
2013/10/18 职场文书
夏季奶茶店创业计划书
2014/01/16 职场文书
授权委托书格式模板
2014/04/03 职场文书
大班下学期幼儿评语
2014/12/30 职场文书
干部培训工作总结2015
2015/05/25 职场文书
漫画「狩龙人拉格纳」公开TV动画预告图
2022/03/22 日漫
python标准库ElementTree处理xml
2022/05/20 Python
华为HarmonyOS3.0强在哪? 看看鸿蒙3.0这7个小功能
2023/01/09 数码科技