不进入抓挡

时间:2019-02-10 07:25:01

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

我有一个带有Transaction的SQL存储过程。 我实际上是分三个阶段编写SP的,每个阶段在不同的表中执行一些操作。 问题是,当SP进入第3阶段时,其中一列是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

1 个答案:

答案 0 :(得分:2)

您可以清楚地看到,如果传递的字符串的长度大于允许的长度,则会出现以下错误,并且交易将回滚:

  

消息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];

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