Sql server 如何获取在存储过程中执行的存储过程的自定义错误?
我目前正在研究如何处理SQL server中嵌套存储过程的异常 不幸的是,遇到了一个场景,我想在存储过程[1]内部执行的存储过程[2]上输出自定义错误 以下是一个例子:Sql server 如何获取在存储过程中执行的存储过程的自定义错误?,sql-server,stored-procedures,Sql Server,Stored Procedures,我目前正在研究如何处理SQL server中嵌套存储过程的异常 不幸的是,遇到了一个场景,我想在存储过程[1]内部执行的存储过程[2]上输出自定义错误 以下是一个例子: /* create the master-table */ CREATE TABLE master_table ( pk_id INT IDENTITY(5,5), access_num INT UNIQUE NOT NULL, first_name NVARCHAR(MAX) PRIMA
/*
create the master-table
*/
CREATE TABLE master_table
(
pk_id INT IDENTITY(5,5),
access_num INT UNIQUE NOT NULL,
first_name NVARCHAR(MAX)
PRIMARY KEY (pk_id)
)
GO
/*
create the child-table
*/
CREATE TABLE child_table
(
pk_id INT IDENTITY(1,1),
val INT UNIQUE,
msgs NVARCHAR(MAX) NOT NULL
PRIMARY KEY (pk_id)
)
GO
/*
create the master procedure
*/
CREATE or ALTER PROC uspInsertIntoMaster
@AccessNum INT,
@FirstName NVARCHAR(MAX),
@Message NVARCHAR(MAX)
AS
BEGIN
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
INSERT INTO master_table (access_num, first_name)
VALUES (@AccessNum, @FirstName)
EXEC uspInsertIntoChild 4, @Message;
IF (@@TRANCOUNT = 1)
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK;
THROW;
IF (XACT_STATE()) = -1
BEGIN
ROLLBACK TRANSACTION;
END;
IF (XACT_STATE()) = 1
BEGIN
COMMIT TRANSACTION;
END;
END CATCH
END
GO
/*
create the child procedure
*/
CREATE or ALTER PROC uspInsertIntoChild
@Value INT,
@Message NVARCHAR(MAX)
AS
DECLARE @ErrorMessage NVARCHAR(2048);
BEGIN
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
BEGIN
SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
THROW 50010, @ErrorMessage, 1;
END;
ELSE
BEGIN TRAN
INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)
IF (@@TRANCOUNT > 0)
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK;
THROW;
IF (XACT_STATE()) = -1
BEGIN
ROLLBACK TRANSACTION;
END;
IF (XACT_STATE()) = 1
BEGIN
COMMIT TRANSACTION;
END;
END CATCH
END
GO
自定义错误消息
EXEC sys.sp_addmessage
@msgnum = 50010,
@severity = 16,
@msgtext = N'Invalid Input. Error occurred at procedure uspInsertIntoChild',
@lang = 'us_english';
GO
EXEC sys.sp_addmessage
@msgnum = 50010,
@severity = 16,
@msgtext =
N'Invalid Input %i. Error occured at procedure uspInsertIntoChild', /*Mobile number [%s] is invalid. Input a valid number*/
@lang = 'us_english';
GO
如果我尝试执行下面的代码。我希望它在第二次执行时失败,并抛出上面的自定义错误
EXEC uspInsertIntoMaster 4, N'miKeL', N'Get Well Soon'
EXEC uspInsertIntoMaster 5, N'miKeL', N'Get Well Soon'
但不幸的是,我没有自定义错误
Msg 50010,16级,状态1,程序uspInsertIntoChild,第14行
[批处理起始行105]错误:50010,严重性:16,状态:1。
(参数:)。错误以简洁模式打印,因为存在错误
在格式化期间。跳过跟踪、ETW、通知等
编辑(@Charlieface) 首先,我先从
master proc
中删除了TRY/CATCH
块,然后删除了这两个块,但仍然出现相同的错误。我还按照@M.Ali的建议,删除了没有INFOMSGS的DBCC dropcleanbuffer
新建主程序
CREATE or ALTER PROC uspInsertIntoMaster
@AccessNum INT,
@FirstName NVARCHAR(30),
@Message NVARCHAR(MAX)
AS
BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRANSACTION
INSERT INTO master_table (access_num, first_name)
VALUES (@AccessNum, @FirstName)
EXEC uspInsertIntoChild 4, @Message;
IF (@@TRANCOUNT = 1)
COMMIT TRANSACTION
END
GO
CREATE or ALTER PROC uspInsertIntoChild
@Value INT,
@Message NVARCHAR(MAX)
AS
DECLARE @ErrorMessage NVARCHAR(MAX);
BEGIN
SET XACT_ABORT, NOCOUNT ON;
IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
BEGIN
SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
-- I want to thow
THROW 50010, @ErrorMessage, 1;
END;
ELSE
BEGIN TRANSACTION
INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)
IF (@@TRANCOUNT > 0)
COMMIT TRANSACTION
END
GO
新建子进程
CREATE or ALTER PROC uspInsertIntoMaster
@AccessNum INT,
@FirstName NVARCHAR(30),
@Message NVARCHAR(MAX)
AS
BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRANSACTION
INSERT INTO master_table (access_num, first_name)
VALUES (@AccessNum, @FirstName)
EXEC uspInsertIntoChild 4, @Message;
IF (@@TRANCOUNT = 1)
COMMIT TRANSACTION
END
GO
CREATE or ALTER PROC uspInsertIntoChild
@Value INT,
@Message NVARCHAR(MAX)
AS
DECLARE @ErrorMessage NVARCHAR(MAX);
BEGIN
SET XACT_ABORT, NOCOUNT ON;
IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
BEGIN
SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
-- I want to thow
THROW 50010, @ErrorMessage, 1;
END;
ELSE
BEGIN TRANSACTION
INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)
IF (@@TRANCOUNT > 0)
COMMIT TRANSACTION
END
GO
编辑2
我真傻。我编辑了格式化的消息。但从未想过还要编辑表示字符串的控制字符%s
。我试图向错误消息传递一个整数参数,因此它应该是%I
最终格式化
错误消息
EXEC sys.sp_addmessage
@msgnum = 50010,
@severity = 16,
@msgtext = N'Invalid Input. Error occurred at procedure uspInsertIntoChild',
@lang = 'us_english';
GO
EXEC sys.sp_addmessage
@msgnum = 50010,
@severity = 16,
@msgtext =
N'Invalid Input %i. Error occured at procedure uspInsertIntoChild', /*Mobile number [%s] is invalid. Input a valid number*/
@lang = 'us_english';
GO
您正在使用的消息没有任何格式参数,因此
FORMATMESSAGE
引发错误。
你需要这样的东西:
EXEC sys.sp\u addmessage
@msgnum=50010,
@严重性=16,
@msgtext=N'输入%s无效。过程uspInsertIntoChild'发生错误,
@lang=‘美国英语’;
顺便说一句,如果您在上使用
XACT\u ABORT,则无需捕获和回滚。
仅当您使用自定义日志记录时才有必要,在本例中,您不需要
此外,由于其他原因,您当前使用的代码不起作用:
- 在
中,您通常希望CATCH
neverROLLBACK
COMMIT
- 如果
则XACT\u STATE=-1
回滚将失败
抛出后不执行代码代码>无论如何
所以我想说删除整个
TRY/CATCH
,保留XACT\u ABORT ON
并记录消息,就像使用sp\u addmessage
一样,为什么不使用抛出自定义错误消息呢?文档中提供了大量的示例来向您展示如何做到这一点:O只是出于兴趣,为什么您有DBCC DROPCLEANBUFFERS而没有任何infomsg代码>在您的过程定义中?我希望您理解此命令的含义,并意识到它可能对您的应用程序和服务器速度造成的危害。此外,错误消息的长度不能超过10亿个字符,它们的上限为2048个字符。您的提交事务位于Catch Block
中,它应该位于Try Block
中。请查看此答案,它将帮助您了解Try和Catch Block以及raiserror/throw函数的正确用法first\u name NVARCHAR(MAX)
。真正地谁的名字这么长
CREATE or ALTER PROC uspInsertIntoMaster
@AccessNum INT,
@FirstName NVARCHAR(30),
@Message NVARCHAR(MAX)
AS
BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRANSACTION
INSERT INTO master_table (access_num, first_name)
VALUES (@AccessNum, @FirstName)
EXEC uspInsertIntoChild 4, @Message;
IF (@@TRANCOUNT = 1)
COMMIT TRANSACTION
END
GO
CREATE or ALTER PROC uspInsertIntoChild
@Value INT,
@Message NVARCHAR(MAX)
AS
DECLARE @ErrorMessage NVARCHAR(MAX);
BEGIN
SET XACT_ABORT, NOCOUNT ON;
IF EXISTS(SELECT 1 FROM child_table WHERE val = @Value)
BEGIN
SET @ErrorMessage = FORMATMESSAGE(50010, @Value);
-- I want to thow
THROW 50010, @ErrorMessage, 1;
END;
ELSE
BEGIN TRANSACTION
INSERT INTO child_table (val, msgs) VALUES (@Value, @Message)
IF (@@TRANCOUNT > 0)
COMMIT TRANSACTION
END
GO