目录:

解读错误信息

RAISERROR

THROW

实例

使用 @@ERROR

使用 XACT_ABORT

使用TRY/CATCH

现实中的事务语句

删除

更新

银行取钱

解读错误信息

Msg 547, Level 16, State 0, Line 11
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Products_Categories".
The conflict occurred in database "TSQL2012", table "Production.Categories", column 'categoryid'.

Error number 

  ● SQL  Server 错误信息的编号从1~49999

  ● 自定义错误信息从50001开始

  ● 错误编号50000是为没有错误编号的自定义信息准备的。

Severity  level

SQL Server 一共26个严重级别  0~25。

  ● 严重级别>= 16的会记录SQL Server日志和Windows 应用程序日志

  ● 严重级别19~25 只能由 sysadmin觉得的成员处理

  ● 严重级别20~25被认为是致命错误。 会中断终端连接并回滚所有打开的事务。

  ● 严重级别0~10只是提示信息。

State  int 类型,最大值127, MS internal purposes

Error message  支持255个Unicode 字符

  ●  SQL  Server 错误信息都在  sys.messages里面

  ●  可以用sp_addmessage 添加自定义错误信息

RAISERROR(不会中断事务)

简单的传递信息可以使用级别0~9 。

如果你有sysadmin的角色,可以使用WITH LOG选项并设置一个严重级别>20的错误。error 发生的时候SQL Server会中断连接。

使用NOWAIT选项可以直接发送信息,而不用等大赛buffer

RAISERROR ('Error in usp_InsertCategories stored procedure', 16, 0);

-- Formatting the RAISERROR string
RAISERROR ('Error in % stored procedure', 16, 0, N'usp_InsertCategories'); -- In addition, you can use a variable:
GO
DECLARE @message AS NVARCHAR(1000) = N'Error in % stored procedure';
RAISERROR (@message, 16, 0, N'usp_InsertCategories'); -- And you can add the formatting outside RAISERROR using the FORMATMESSAGE function:
GO
DECLARE @message AS NVARCHAR(1000) = N'Error in % stored procedure';
SELECT @message = FORMATMESSAGE (@message, N'usp_InsertCategories');
RAISERROR (@message, 16, 0);

THROW (会中断事务)

-- You can issue a simple THROW as follows:
THROW 50000, 'Error in usp_InsertCategories stored procedure', 0; -- Because THROW does not allow formatting of the message parameter, you can use FORMATMESSAGE()
GO
DECLARE @message AS NVARCHAR(1000) = N'Error in % stored procedure';
SELECT @message = FORMATMESSAGE (@message, N'usp_InsertCategories');
THROW 50000, @message, 0;
-- RAISERROR does not normally terminate a batch:
RAISERROR ('Hi there', 16, 0);
PRINT 'RAISERROR error'; -- Prints
GO -- However, THROW does terminate the batch:
THROW 50000, 'Hi there', 0;
PRINT 'THROW error'; -- Does not print
GO

实例

使用 @@ERROR

DECLARE @errnum AS int;
BEGIN TRAN;
SET IDENTITY_INSERT Production.Products ON;
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
SET @errnum = @@ERROR;
IF @errnum <> 0 -- Handle the error
BEGIN
PRINT 'Insert into Production.Products failed with error ' + CAST(@errnum AS VARCHAR);
END
DECLARE @errnum AS int;
BEGIN TRAN;
SET IDENTITY_INSERT Production.Products ON;
-- Insert #1 will fail because of duplicate primary key
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
SET @errnum = @@ERROR;
IF @errnum <> 0
BEGIN
IF @@TRANCOUNT > 0 ROLLBACK TRAN;
PRINT 'Insert #1 into Production.Products failed with error ' + CAST(@errnum AS VARCHAR);
END;
-- Insert #2 will succeed
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 1, 18.00, 0);
SET @errnum = @@ERROR;
IF @errnum <> 0
BEGIN
IF @@TRANCOUNT > 0 ROLLBACK TRAN;
PRINT 'Insert #2 into Production.Products failed with error ' + CAST(@errnum AS VARCHAR);
END;
SET IDENTITY_INSERT Production.Products OFF;
IF @@TRANCOUNT > 0 COMMIT TRAN;
-- Remove the inserted row
DELETE FROM Production.Products WHERE productid = 101;
PRINT 'Deleted ' + CAST(@@ROWCOUNT AS VARCHAR) + ' rows';

使用 XACT_ABORT

使用XACT_ABORT,语句中发生错误,整段语句都会中止。

SET XACT_ABORT ON;
PRINT 'Before error';
SET IDENTITY_INSERT Production.Products ON;
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
SET IDENTITY_INSERT Production.Products OFF;
PRINT 'After error';
GO
PRINT 'New batch';
SET XACT_ABORT OFF;
-- Using THROW with XACT_ABORT.
USE TSQL2012;
GO
SET XACT_ABORT ON;
PRINT 'Before error';
THROW 50000, 'Error in usp_InsertCategories stored procedure', 0;
PRINT 'After error';
GO
PRINT 'New batch';
SET XACT_ABORT OFF;

@@ERROR第二个例子中使用XACT_ABORT以后,第二条语句这回就无效了。

DECLARE @errnum AS int;
SET XACT_ABORT ON;
BEGIN TRAN;
SET IDENTITY_INSERT Production.Products ON;
-- Insert #1 will fail because of duplicate primary key
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
SET @errnum = @@ERROR;
IF @errnum <> 0
BEGIN
IF @@TRANCOUNT > 0 ROLLBACK TRAN;
PRINT 'Error in first INSERT';
END;
-- Insert #2 no longer succeeds
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 1, 18.00, 0);
SET @errnum = @@ERROR;
IF @errnum <> 0
BEGIN
-- Take actions based on the error
IF @@TRANCOUNT > 0 ROLLBACK TRAN;
PRINT 'Error in second INSERT';
END;
SET IDENTITY_INSERT Production.Products OFF;
IF @@TRANCOUNT > 0 COMMIT TRAN;
GO DELETE FROM Production.Products WHERE productid = 101;
PRINT 'Deleted ' + CAST(@@ROWCOUNT AS VARCHAR) + ' rows';
SET XACT_ABORT OFF;
GO
SELECT XACT_STATE(), @@TRANCOUNT;

使用TRY/CATCH

格式

--Transactions extend batches
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO Sales.SalesOrderHeader... --Succeeds
INSERT INTO Sales.SalesOrderDetail... --Fails
COMMIT TRANSACTION -- If no errors, transaction completes
END TRY
BEGIN CATCH
--Inserted rows still exist in Sales.SalesOrderHeader SELECT ERROR_NUMBER()
ROLLBACK TRANSACTION --Any transaction work undone
END CATCH;
BEGIN TRY
BEGIN TRAN;
SET IDENTITY_INSERT Production.Products ON;
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 10, 18.00, 0);
SET IDENTITY_INSERT Production.Products OFF;
COMMIT TRAN;
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627 -- Duplicate key violation
BEGIN
PRINT 'Primary Key violation';
END
ELSE IF ERROR_NUMBER() = 547 -- Constraint violations
BEGIN
PRINT 'Constraint violation';
END
ELSE
BEGIN
PRINT 'Unhandled error';
END;
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH;
-- revise the CATCH block using variables to capture error information and re-raise the error using RAISERROR.
USE TSQL2012;
GO
SET NOCOUNT ON;
DECLARE @error_number AS INT, @error_message AS NVARCHAR(1000), @error_severity AS INT;
BEGIN TRY
BEGIN TRAN;
SET IDENTITY_INSERT Production.Products ON;
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 10, 18.00, 0);
SET IDENTITY_INSERT Production.Products OFF;
COMMIT TRAN;
END TRY
BEGIN CATCH
SELECT XACT_STATE() as 'XACT_STATE', @@TRANCOUNT as '@@TRANCOUNT';
SELECT @error_number = ERROR_NUMBER(), @error_message = ERROR_MESSAGE(), @error_severity = ERROR_SEVERITY();
RAISERROR (@error_message, @error_severity, 1);
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH;
-- use a THROW statement without parameters re-raise (re-throw) the original error message and send it back to the client.
USE TSQL2012;
GO
BEGIN TRY
BEGIN TRAN;
SET IDENTITY_INSERT Production.Products ON;
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
INSERT INTO Production.Products(productid, productname, supplierid, categoryid, unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 10, 18.00, 0);
SET IDENTITY_INSERT Production.Products OFF;
COMMIT TRAN;
END TRY
BEGIN CATCH
SELECT XACT_STATE() as 'XACT_STATE', @@TRANCOUNT as '@@TRANCOUNT';
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
THROW;
END CATCH;
GO
SELECT XACT_STATE() as 'XACT_STATE', @@TRANCOUNT as '@@TRANCOUNT';

现实中的事务语句

删除

--删除
CREATE PROCEDURE [dbo].[Students_Delete](@ID int)
WITH EXECUTE AS CALLER
AS
BEGIN
--Check to make sure the ID does exist
--If not does, return error
DECLARE @existing AS int = 0
SELECT @existing = count(ID)
FROM Students
WHERE ID = @ID IF @existing <> 1
BEGIN
RAISERROR ('ID does not exist', 1, 1)
RETURN 0
END
--Attempt Delete
DELETE FROM [dbo].[Students]
WHERE ID = @ID --check to see if update occured
--and return status
IF @@ROWCOUNT = 1
BEGIN
INSERT INTO StudentDeleteLog
VALUES (suser_sname(), @ID, getdate())
RETURN 1
END ELSE
RETURN 0
END
GO

更新

CREATE PROCEDURE [dbo].[Students_Update]
( @ID int,
@LASTNAME varchar(50),
@FIRSTNAME varchar(50),
@STATE varchar(50),
@PHONE varchar(50),
@EMAIL varchar(50),
@GRADYEAR int,
@GPA decimal(20,10),
@PROGRAM varchar(50),
@NEWSLETTER bit
)
AS
BEGIN
--Check to make sure the ID does exist
--If not does, return error
DECLARE @existing AS int = 0
SELECT @existing = count(ID)
FROM Students
WHERE ID = @ID IF @existing <> 1
BEGIN
RAISERROR ('ID does not exist', 1, 1)
RETURN 0
END
--Can not subscribe to newsletter if email is null
IF (@email IS NULL)
SET @NEWSLETTER = 0 --Attempt Update
UPDATE [dbo].[Students]
SET [LASTNAME] = @LASTNAME
,[FIRSTNAME] = @FIRSTNAME
,[STATE] = @STATE
,[PHONE] = @PHONE
,[EMAIL] = @EMAIL
,[GRADYEAR] = @GRADYEAR
,[GPA] = @GPA
,[PROGRAM] = @PROGRAM
,[NEWSLETTER] = @NEWSLETTER
WHERE ID = @ID --check to see if update occured
--and return status
IF @@ROWCOUNT = 1
RETURN 1
ELSE
RETURN 0
END
GO

银行取钱

BEGIN TRAN;
IF NOT EXISTS (
SELECT * FROM Accounts WITH(UPDLOCK) --只有当前的事务可以查看
WHERE AccountID = 47387438 AND Balance >= 400
)
BEGIN
ROOLBACK TRAN;
THROW 50000,'Tobias is too poor',1;
END
UPDATE Accounts SET
Balance -=400
WHERE AccountID = 47387438;
COMMIT TRAN; --银行取钱高效版本
BEGIN TRAN;
UPDATE Accounts SET
Balance -= 400
WHERE AccountID = 47387438 AND Balance >= 400
IF(@@ROWCOUNT <> 1)
BEGIN
ROLLBACK TRAN;
THROW 50000,'Tobias is too poor ',1;
END
COMMIT TRAN;

参考文档

Database Engine Error Severities

https://msdn.microsoft.com/en-us/library/ms164086.aspx

SET XACT_ABORT (Transact-SQL)

https://msdn.microsoft.com/zh-tw/library/ms188792.aspx

 
 

SQL Server:错误处理及事务控制的更多相关文章

  1. SQL Server 2014新特性——事务持久性控制

    控制事务持久性 SQL Server 2014之后事务分为2种:完全持久, 默认或延迟的持久. 完全持久,当事务被提交之后,会把事务日志写入到磁盘,完成后返回给客户端. 延迟持久,事务提交是异步的,在 ...

  2. SQL SERVER错误:已超过了锁请求超时时段。 (Microsoft SQL Server,错误: 1222)

    在SSMS(Microsoft SQL Server Management Studio)里面,查看数据库对应的表的时候,会遇到"Lock Request time out period e ...

  3. SQL Server自动化运维系列——监控磁盘剩余空间及SQL Server错误日志(Power Shell)

    需求描述 在我们的生产环境中,大部分情况下需要有自己的运维体制,包括自己健康状态的检测等.如果发生异常,需要提前预警的,通知形式一般为发邮件告知. 在所有的自检流程中最基础的一个就是磁盘剩余空间检测. ...

  4. SQL Server 错误日志收缩(ERRORLOG)

    一.基础知识 默认情况下,错误日志位于 : C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\LOG\ERRORLOG 和ERRORLOG.n 文 ...

  5. SQL Server自动化运维系列 - 监控磁盘剩余空间及SQL Server错误日志(Power Shell)

    需求描述 在我们的生产环境中,大部分情况下需要有自己的运维体制,包括自己健康状态的检测等.如果发生异常,需要提前预警的,通知形式一般为发邮件告知. 在所有的自检流程中最基础的一个就是磁盘剩余空间检测. ...

  6. sql server 错误日志errorlog

    一 .概述 SQL Server 将某些系统事件和用户定义事件记录到 SQL Server 错误日志和 Microsoft Windows 应用程序日志中. 这两种日志都会自动给所有记录事件加上时间戳 ...

  7. SQL Server 存储过程,带事务的存储过程(创建存储过程,删除存储过程,修改存储过

    存储过程 创建存储过程 use pubs --pubs为数据库 go create procedure MyPRO --procedure为创建存储过程关键字,也可以简写proc,MyPRO为存储过程 ...

  8. SQL Server 自动化运维系列 - 监控磁盘剩余空间及SQL Server错误日志(Power Shell)

    需求描述 在我们的生产环境中,大部分情况下需要有自己的运维体制,包括自己健康状态的检测等.如果发生异常,需要提前预警的,通知形式一般为发邮件告知. 在所有的自检流程中最基础的一个就是磁盘剩余空间检测. ...

  9. sql server 2008启动时:已成功与服务器建立连接,但是在登录过程中发生错误。(provider:命名管道提供程序,error:0-管道的另一端上无任何进程。)(Microsoft SQL Server,错误:233) 然后再连接:错误:18456

    问题:sql server 2008启动时:已成功与服务器建立连接,但是在登录过程中发生错误.(provider:命名管道提供程序,error:0-管道的另一端上无任何进程.)(Microsoft S ...

随机推荐

  1. printf不同格式表示法

    格式代码 A ABC ABCDEFGH %S A ABC ABCDEFGH %5S ####A ##ABC ABCDEFGH %.5S A ABC ABCDE %5.5S ####A ##ABC AB ...

  2. C#进程管理程序实现

    运行效果图 部分代码如下: #region 打开应用程序按钮事件处理程序 /// <summary> /// 打开应用程序按钮事件处理程序 /// </summary> /// ...

  3. Core第三方开源Web框架

    NET Core第三方开源Web框架YOYOFx   YOYOFx框架 YOYOFx是一个轻量级用于构建基于 HTTP 的 Web 服务,基于 .NET 和 Mono 平台. 本着学习的态度,造了这个 ...

  4. Codeforces Gym10008E Harmonious Matrices(高斯消元)

    [题目链接] http://codeforces.com/gym/100008/ [题目大意] 给出 一个n*m的矩阵,要求用0和1填满,使得每个位置和周围四格相加为偶数,要求1的数目尽量多. [题解 ...

  5. [LeetCode] Best Time to Buy and Sell Stock Solution

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  6. 自定义View编译失败。Binary XML file line #255: Error inflating

    02-28 15:17:16.281: DEBUG/AndroidRuntime(391): Shutting down VM 02-28 15:17:16.281: WARN/dalvikvm(39 ...

  7. linux之多进程fork:进程通信

    ++++++++++++++++++信号机制+++++++++++++++++++ 接收信号 int signal(int sig,__sighandler_t handler); int func( ...

  8. [置顶] 内存映射失败MapViewOfFile 失败 返回 8

    问题描述1 在使用内存映射方式读写数据时,将文件A的内容拷贝至文件B中,偶尔会出来文件拷贝后的文件,内容为空,或部分为空 问题分析1 怀疑是内存映射方式读写数据的稳定性(可笑的怀疑,内存映射可以Win ...

  9. ICE

    一.Slice-to-C++映射 1.引言 其映射定义:怎样把Slice数据类型翻译成C++类型,客户怎样调用操作.传递参数.处理错误. C++映射完全是线程安全的.例如,类的引用机制针对并行访问机制 ...

  10. BootStrap 智能表单系列 十一 级联下拉的支持

    像省市县选择的这种,但凡是个人肯定都见过,实现方式有很多种 1.有在第一级选择的时候去加载或者从本地对象中拿第一级对应的数据源显示到列表中,第二级以此类推 2.也有将所有的项都加载到select中,然 ...