【问题标题】:not getting into Catch block没有进入 Catch 块
【发布时间】:2019-02-10 07:25:01
【问题描述】:

我有一个带有Transaction 的SQL 存储过程。 我实际上用三个阶段编写了 SP,每个阶段都在不同的表中做一些事情。 问题是,当 SP 进入第三阶段时,其中一列是 varchar(20),但我插入了一个包含 30 个字符的字符串,尽管 SP 成功完成,但我希望它进入 Catch 块并制作一个ROLLBACK。 第三阶段没有提交,也没有向表中添加任何行,但是,感觉就像Transcation 没有工作,两个第一阶段都提交了。

这是我的 SQL SP:

USE [dbfoo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO


ALTER PROCEDURE [dbo].[io_sp_admin]
@id BIGINT = 0, @firstName VARCHAR(20),@lastName VARCHAR(20) ,@email VARCHAR(50), @birthDate DATETIME
        
AS
BEGIN
       -- SET NOCOUNT ON added to prevent extra result sets from
       -- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION [TranAddEmp]
OPEN SYMMETRIC KEY io_key DECRYPTION BY CERTIFICATE foo

--stage1
DECLARE @identity BIGINT = 0 

INSERT INTO [dbo].[t1]
           ([id],
            [deleted],
            [user_name],
     VALUES
           (EncryptByKey(KEY_GUID('io_key'), CONVERT(VARBINARY(100),CAST(@id AS VARCHAR(10)))),
           0,
           'IoAdmin'

SELECT @identity = @@identity 

--satge2
INSERT INTO [dbo].[t2]
           ([id]
           ,[deleted]
           ,[user_name]
     VALUES
           (@identity,
           EncryptByKey(KEY_GUID('io_key'), CONVERT(VARBINARY(100),CAST(@ms_zehut AS VARCHAR(10)))),
           0,
           'IoAdmin'

--stage 3
INSERT INTO t3(
id,
lastName,
firstName,
birtdate,
email)

SELECT
 @identity,
 @lastName , 
 @firstName , 
@birthDate,
 @email 

CLOSE ALL SYMMETRIC KEYS
           
SELECT CAST(1 as BIT) as 'Status', 'Succeeded' as 'ReturnMessage'   
COMMIT TRANSACTION [TranAddEmp]     
END TRY

BEGIN CATCH
 
SELECT CAST(0 as BIT) as 'Status', 'ADMIN - Add employee failed' as 'ReturnMessage'       
ROLLBACK TRANSACTION [TranAddEmp]     
END CATCH
END

【问题讨论】:

  • 旁白:@@Identity 可以在存在触发器时做一些有趣的事情。 Scope_Identity() 是更好的选择。当涉及多个值时,output 子句是一件很棒的事情。
  • 10x 老兄..我去看看

标签: sql sql-server tsql error-handling transactions


【解决方案1】:

您可以清楚地看到,如果您传递一个长度大于允许的字符串,您将收到以下错误,并且执行了事务回滚:

消息 8152,级别 16,状态 30,行 18 字符串或二进制数据将是 被截断。

DROP TABLE IF EXISTS [dbo].[DataSource];

CREATE TABLE [dbo].[DataSource]
(
    [value] VARCHAR(8)
);

BEGIN TRY

    BEGIN TRANSACTION

    INSERT INTO [dbo].[DataSource] ([value])
    VALUES ('123');

    INSERT INTO [dbo].[DataSource] ([value])
    VALUES ('very large string');

    COMMIT TRANSACTION

END TRY
BEGIN CATCH

    IF @@TRANCOUNT > 0
    BEGIN;
        ROLLBACK TRANSACTION;
    END;

    THROW;

END CATCH

SELECT *
FROM [dbo].[DataSource];

所以,您的代码没有任何问题。但是您正在通过变量传递字符串,并且值会自动截断以适应可变长度。

所以,请检查以下内容:

DROP TABLE IF EXISTS [dbo].[DataSource];

CREATE TABLE [dbo].[DataSource]
(
    [value] VARCHAR(8)
);

DECLARE @values VARCHAR(8) = 'very large string';

SELECT @values;

BEGIN TRY

    BEGIN TRANSACTION

    INSERT INTO [dbo].[DataSource] ([value])
    VALUES ('123');

    INSERT INTO [dbo].[DataSource] ([value])
    VALUES (@values);

    COMMIT TRANSACTION

END TRY
BEGIN CATCH

    IF @@TRANCOUNT > 0
    BEGIN;
        ROLLBACK TRANSACTION;
    END;

    THROW;

END CATCH

SELECT *
FROM [dbo].[DataSource];

因此,您可以在调用存储过程之前检查输入参数是否有效,或者增加输入参数的值并依赖引擎。无论如何,我更喜欢在使用数据之前对其进行验证——这对我来说似乎更清楚(如果不允许,为什么我们允许用户输入更长的名称?)。

【讨论】:

    猜你喜欢
    • 2013-03-27
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-30
    • 2019-03-03
    • 1970-01-01
    • 2012-03-06
    相关资源
    最近更新 更多