通过procureure插入数据面临意外问题

时间:2015-06-16 11:06:18

标签: sql sql-server

我创建了将数据插入表格的程序 测试:

CREATE TABLE TEST
(
Id INT, 
Name VARCHAR(5)
)

CREATE PROCEDURE usp_insert
(
@i_id INT,
@vc_name VARCHAR(10)
)
AS 
BEGIN 
BEGIN TRANSACTION  
SET NOCOUNT ON ;  

BEGIN TRY 
DECLARE  @new_identity INT; 
INSERT INTO dbo.Test
        (Id,
        name)
VALUES 
(@i_id,
@vc_name)
    SELECT @new_identity = SCOPE_IDENTITY()  

    SELECT @new_identity   

    RETURN(@new_identity)  
    COMMIT TRANSACTION 
COMMIT TRAN
END TRY 
BEGIN CATCH
    SELECT ERROR_NUMBER() AS ErrorNumber  
     ,ERROR_SEVERITY() AS ErrorSeverity  
     ,ERROR_STATE() AS ErrorState  
     ,ERROR_PROCEDURE() AS ErrorProcedure  
     ,ERROR_LINE() AS ErrorLine  
     ,ERROR_MESSAGE() AS ErrorMessage;  
     ROLLBACK TRANSACTION  

END CATCH
END

插入数据时我收到如下错误

EXEC usp_insert 1,'mohan'

The 'usp_insert' procedure attempted to return a status of NULL, which is not allowed. A status of 0 will be returned instead.
Msg 266, Level 16, State 2, Procedure usp_insert, Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.

但数据正在插入 和

尝试插入更多长度名称以查看它在数据之前删除的异常,并在第二次尝试时显示异常

EXEC usp_insert 1,'mohankumar'

Msg 266, Level 16, State 2, Procedure usp_insert, Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.

2 个答案:

答案 0 :(得分:2)

致电时

EXEC usp_insert 1,'mohankumar'
  

' usp_insert'过程尝试返回NULL状态,这是不允许的。将返回状态0。

     

消息266,级别16,状态2,过程usp_insert,第0行   EXECUTE之后的事务计数表示BEGIN和COMMIT语句的数量不匹配。先前的计数= 0,当前计数= 1。

您收到警告,因为您的表格TEST没有identity列,因此SELECT @new_identity = SCOPE_IDENTITY()始终将@new_identity设置为NULL,因为@new_identity返回1}},您从Sql Server收到警告。

您收到事务计数错误,因为控件到达RETURN语句时存在该过程,并且未执行COMMIT TRANSACTION。因此,在执行过程之前和之后存在事务计数的不匹配。

您可能认为该过程已保存数据,但事实并非如此,因为事务仍处于打开状态,如果您发出ROLLBACK,则数据将被回滚。尝试从另一个会话中检查表格中的数据,并在尝试访问该表格时被阻止。

第二次电话

EXEC usp_insert 1,'mohankumar'
  

消息266,级别16,状态2,过程usp_insert,第0行   EXECUTE之后的事务计数表示BEGIN和COMMIT语句的数量不匹配。先前的计数= 1,当前计数= 0。

上一个事务已经打开,并且由于参数的长度比新行长,因此插入失败,流程将移至catch回滚事务的块。

这是第一次调用usp_insert时启动的事务,看起来第二次调用删除了上一个插入的数据,但插件从未提交过。

前进之路

  1. 如果您想使用SCOPE_IDENTITY(),您的表格应该有一个IDENTITY列。如果表中有标识列,那么您不需要将变量传递给标识列的过程
  2. 不要使用RETURN作为将数据(或插入的id)返回给应用程序的方法。使用SELECTOUTPUT参数。
  3. RETURN应该是BEGIN TRY中的最后一个语句(除非您有条件检查)。任何COMMIT / ROLLBACK /业务逻辑应该在RETURN
  4. 之前
  5. 不要将错误消息作为SELECT返回。 ROLLBACK您的事务,记录它(如果需要)然后将其丢回应用程序并让应用程序处理错误
  6. 更新程序

    CREATE PROCEDURE usp_insert
    (
    @i_id INT,
    @vc_name VARCHAR(10)
    )
    AS 
    BEGIN 
    SET NOCOUNT ON;
    BEGIN TRY 
    
    INSERT INTO dbo.Test(Id,name)
    VALUES(@i_id,@vc_name)
    
    END TRY 
    BEGIN CATCH
        INSERT Into LogTable
        SELECT ERROR_NUMBER() AS ErrorNumber  
         ,ERROR_SEVERITY() AS ErrorSeverity  
         ,ERROR_STATE() AS ErrorState  
         ,ERROR_PROCEDURE() AS ErrorProcedure  
         ,ERROR_LINE() AS ErrorLine  
         ,ERROR_MESSAGE() AS ErrorMessage; 
    
        ROLLBACK TRANSACTION;
    
        THROW; -- You can use RAISERROR if your sql server version doesn't support THROW
    
    END CATCH
    END
    

    注意:

    1. 删除了BEGIN TRANCOMMIT,因为它只是一个DML语句,当前代码不需要显式事务边界。
    2. 删除了使用RETURN,因为您已经知道要保存的ID。
    3. 如果需要,将错误消息记录在表中,否则只需将它们返回到调用应用程序

答案 1 :(得分:0)

编写相同功能的更好方法是:

CREATE TABLE TEST
(
Id INT IDENTITY (1,1), -- declare Identity property to auto-increment Id 
Name VARCHAR(5)
)
Go

CREATE PROCEDURE usp_insert
@vc_name VARCHAR(10),
@new_identity Int OUTPUT -- Explicitly declare the returned value
AS 
BEGIN 
BEGIN TRANSACTION  
SET NOCOUNT ON ; 

        BEGIN TRY               
                INSERT INTO Test
                        (name)
                VALUES 
                (@vc_name)
                SELECT @new_identity = SCOPE_IDENTITY()  

                SELECT @new_identity   
                COMMIT TRANSACTION
                RETURN(@new_identity)  

                --COMMIT TRAN -- where is this coming from?
        END TRY 
        BEGIN CATCH
            SELECT ERROR_NUMBER() AS ErrorNumber  
             ,ERROR_SEVERITY() AS ErrorSeverity  
             ,ERROR_STATE() AS ErrorState  
             ,ERROR_PROCEDURE() AS ErrorProcedure  
             ,ERROR_LINE() AS ErrorLine  
             ,ERROR_MESSAGE() AS ErrorMessage;  
             ROLLBACK TRANSACTION  

        END CATCH
END

GO-- Declare the variable to receive the output value of the procedure.
DECLARE @Id Int;
-- Execute the procedure specifying a name for the input parameter
-- and saving the output value in the variable @Id
EXECUTE usp_insert
    'Mohan', @new_identity = @Id OUTPUT;
-- Display the value returned by the procedure.
PRINT 'New Id for this employee is ' + 
    convert(varchar(10),@Id);
GO

Demo