Почему эта транзакция SQL Server всегда терпит неудачу и как исправить структуру хранимой процедуры? - PullRequest
0 голосов
/ 23 марта 2012

У меня возникли проблемы с хранимой процедурой, в которой транзакция всегда завершается неудачей. Я думаю, что это из-за IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId) или из-за структуры кода, но я не уверен, как исправить хранимую процедуру, чтобы транзакция не всегда заканчивалась неудачей. Когда я удаляю весь код транзакции и выполняю те же действия без транзакции в хранимой процедуре, она работает. Все обновления и / или вставки выполнены успешно.

CREATE PROCEDURE [dbo].[proc_BaseTable_TargetTable_Update] 
(
@BaseTableId        bigint,
@BaseTableTypeId        bigint,
@Alias      nvarchar(max),
@TargetTableNonPkItemId bigint,
@OtherField     nvarchar(max) = NULL
)
AS
-- NOTE: There is a problem with enabling tractions on this, the trans always fails
-- with the current structure, seemingly because of the SELECT 1 FROM TARGETTABLE
BEGIN
BEGIN TRAN

    UPDATE BaseTable 
        SET 
            Alias = @Alias,
            BaseTableTypeId = @BaseTableTypeId,
            UpdatedOn = GETUTCDATE()
    WHERE BaseTableId = @BaseTableId

    IF @@ERROR <> 0
    BEGIN
        RAISERROR('Error updating BaseTable', 16, 1)
        ROLLBACK TRAN
        RETURN
    END

    IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId)
        UPDATE TargetTable
            SET
                TargetTableNonPkItemId = @TargetTableNonPkItemId,
                OtherField = @OtherField
        WHERE BaseTableId = @BaseTableId

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error updating TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END
    ELSE
        INSERT INTO TargetTable(BaseTableId, TargetTableNonPkItemId, OtherField)
        VALUES (@BaseTableId, @TargetTableNonPkItemId, @OtherField)

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error inserting TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END

COMMIT TRAN
END

Ответы [ 3 ]

3 голосов
/ 23 марта 2012

Вам нужно обернуть IF.Что происходит в вашем коде, так это то, что вы ожидаете что-то вроде этого

IF EXISTS
{
   UPDATE
   RAISE ERROR IF BAD
}
ELSE
{
    INSERT
    RAISE ERROR IF BAD
}

Но вместо этого вы получите

IF EXISTS
    UPDATE


IF ERROR
    RAISE ERROR
ELSE 
    INSERT


IF ERROR
    RAISE ERROR

Итак, вот как должен выглядеть ваш код:

CREATE PROCEDURE [dbo].[proc_BaseTable_TargetTable_Update] 
(
@BaseTableId        bigint,
@BaseTableTypeId        bigint,
@Alias      nvarchar(max),
@TargetTableNonPkItemId bigint,
@OtherField     nvarchar(max) = NULL
)
AS
-- NOTE: There is a problem with enabling tractions on this, the trans always fails
-- with the current structure, seemingly because of the SELECT 1 FROM TARGETTABLE
BEGIN
BEGIN TRAN

    UPDATE BaseTable 
        SET 
            Alias = @Alias,
            BaseTableTypeId = @BaseTableTypeId,
            UpdatedOn = GETUTCDATE()
    WHERE BaseTableId = @BaseTableId

    IF @@ERROR <> 0
    BEGIN
        RAISERROR('Error updating BaseTable', 16, 1)
        ROLLBACK TRAN
        RETURN
    END

    IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId)
    BEGIN
        UPDATE TargetTable
            SET
                TargetTableNonPkItemId = @TargetTableNonPkItemId,
                OtherField = @OtherField
        WHERE BaseTableId = @BaseTableId

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error updating TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END
    END
    ELSE
    BEGIN
        INSERT INTO TargetTable(BaseTableId, TargetTableNonPkItemId, OtherField)
        VALUES (@BaseTableId, @TargetTableNonPkItemId, @OtherField)

        IF @@ERROR <> 0
        BEGIN
            RAISERROR('Error inserting TargetTable', 16, 1)
            ROLLBACK TRAN
            RETURN
        END
    END

COMMIT TRAN
END
2 голосов
/ 23 марта 2012

Вы должны добавить блок BEGIN / END после IF и после ELSE, потому что у вас есть несколько операторов в этих блоках.

1 голос
/ 23 марта 2012

Я предпочитаю использовать TRY CATCH для обработки ошибок.Приведенный ниже пример даст более полезное сообщение об ошибке.

CREATE PROCEDURE [dbo].[proc_BaseTable_TargetTable_Update]  
( @BaseTableId        bigint
, @BaseTableTypeId        bigint
, @Alias      nvarchar(max)
, @TargetTableNonPkItemId bigint
, @OtherField     nvarchar(max) = NULL ) 
AS  

BEGIN TRY
BEGIN TRAN      
    UPDATE BaseTable          
    SET Alias = @Alias
    , BaseTableTypeId = @BaseTableTypeId
    , UpdatedOn = GETUTCDATE()     
    WHERE BaseTableId = @BaseTableId           

IF EXISTS(SELECT 1 FROM TargetTable WHERE BaseTableId = @BaseTableId)         
    UPDATE TargetTable             
    SET TargetTableNonPkItemId = @TargetTableNonPkItemId
        , OtherField = @OtherField         
    WHERE BaseTableId = @BaseTableId         
ELSE         
    INSERT INTO TargetTable(BaseTableId, TargetTableNonPkItemId, OtherField)         
    VALUES (@BaseTableId, @TargetTableNonPkItemId, @OtherField)  

IF @@TRANCOUNT > 0
    BEGIN
        COMMIT TRAN
    END 
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;

IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRAN
    END

END CATCH
...