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