如何在 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 中的异常处理
示例:了解 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 (产品表)
ProductIDNamePriceQuantity101Laptop15000100102Desktop20000150104Mobile3000200105Tablet4000250ProductSales (产品销售表)
ProductSalesIDProductIDQuantitySold110110210215310430410535请使用以下 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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |