在 SQL Server 中使用 Try Catch 处理异常
如何在 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 (产品表)
| 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 处理异常
CREATE TABLE ErrorLog( errNum INT, ErrSev ), ErrState INT, ErrProc ), ErrLine INT, ErrMsg ) ) CREATE ...
- SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- SQL Server中的事务与锁
了解事务和锁 事务:保持逻辑数据一致性与可恢复性,必不可少的利器. 锁:多用户访问同一数据库资源时,对访问的先后次序权限管理的一种机制,没有他事务或许将会一塌糊涂,不能保证数据的安全正确读写. 死锁: ...
- SQL Server中的事物
1.事务的四个属性 原子性Atomicity,一致性Consistency,隔离性Isolation,持久性Durability ,即ACID特性. 原子性:事务必须是一个完整工作的单元,要么全部执行 ...
- SQL Server中事务、锁定和阻塞
事务是什么 在SQL Server中事务是构成一个工作逻辑单元的一系列任务,也就说多个任务放在一起执行,这些任务要么全部执行成功,要么全部执行失败. 通过事务我们可以保证数据的完整性,例如:用户A给用 ...
- 【转】SQL Server中的事务与锁
SQL Server中的事务与锁 了解事务和锁 事务:保持逻辑数据一致性与可恢复性,必不可少的利器. 锁:多用户访问同一数据库资源时,对访问的先后次序权限管理的一种机制,没有他事务或许将会一塌糊涂 ...
- 深入浅出SQL Server中的死锁
简介 死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的.理解死锁首先需要对死锁所涉及的相关观念有一个理解. 一些基础知识 要理解SQL Server中的死锁,更好的方式是通过类比从更大的面理 ...
- SQL Server中解决死锁
SQL Server中解决死锁的新方法介绍 数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server 2005, 现在似乎有了一种新的解决办法. 将下面的 ...
- SQL Server 中关于 @@error 的一个小误区
在SQL Server中,我常常会看到有些前辈这样写: ) ROLLBACK TRANSACTION T else COMMIT TRANSACTION T 一开始,我看见别人这么写,我就想当然的以为 ...
随机推荐
- Bugku练习题---Web---计算器
Bugku练习题---Web---计算器 flag:flag{8b4b2f83db2992d17d770be1db965147} 解题步骤: 1.观察题目,打开场景 2.场景打开后发现是一个验证码界面 ...
- 【译】defer-panic-and-recover
Go 有通用的控制流程:if,for,switch,goto.它也有go语句用于让代码运行在单独的协程.这里我将讨论一些不常见的问题:defer,panic 和 recover. defer语句将函数 ...
- [题解] [AGC022E] Median Replace
题目大意 有个奇数长度的 \(01\) 串 \(s\) 其中有若干位置是 \(?\). 每次可将 \(3\) 个连续的字符替换成这三个数的中位数. 求有多少方案将 \(?\) 替换成 \(0/1\) ...
- Linux系统安装Python3环境
点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 本文基于如下Linux系统版本: 1.默认情况下,Lin ...
- 干货 | Keepalived高可用服务配置实例
一个执着于技术的公众号 Keepalived系列导读 Keepalived入门学习 keepalived安装及配置文件详解 前言 在前面的章节中,我们学习了Keepalived简介.原理.以及Keep ...
- 【Java分享客栈】超简洁SpringBoot使用AOP统一日志管理-纯干货干到便秘
前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国 ...
- 这 BUG,绝了
上周只上了三天班,但我也丝毫不敢懈怠,BUG 更是一个也没少写. 看着满屏幕的 ERROR,我陷入沉思.为什么我写的代如此烂,无法像大牛们写的那般优雅? 越想越自卑,越想越抑郁.我觉得这样不行,一定得 ...
- sklearn机器学习实战-KNN
KNN分类 KNN是惰性学习模型,也被称为基于实例的学习模型 简单线性回归是勤奋学习模型,训练阶段耗费计算资源,但是预测阶段代价不高 首先工作是把label的内容进行二值化(如果多分类任务,则考虑On ...
- Centons7最小化安装报错:ping: baidu.com: Name or service not know
在这之前,centos7最小化安装默认是不能联网的,首先必须切换到root用户,再解决网络问题 一. 切换到root用户 二. 解决网络问题 一.切换到root用户 Linux下 ...
- 用 Go 快速开发一个 RESTful API 服务
何时使用单体 RESTful 服务 对于很多初创公司来说,业务的早期我们更应该关注于业务价值的交付,而单体服务具有架构简单,部署简单,开发成本低等优点,可以帮助我们快速实现产品需求.我们在使用单体服务 ...