2016-02-01 3 views
1

У меня возникли проблемы, которые выглядят как LOT, как транзакция в хранимой процедуре, откат, хотя я уверен, что он был зафиксирован, поскольку выходная переменная не установлена ​​до тех пор, пока не будет выполнена фиксация, и пользователь получает значение выходной переменной (я знаю, потому что они распечатывают ее, и я также настроил таблицу журналов, в которой я вводил значение выходной переменной). Теоретически кто-то МОЖЕТ вручную удалить и обновить данные таким образом, чтобы они выглядели как откат, но это крайне маловероятно.Сделка отменяется после фиксации?

Итак, я надеюсь, кто-то может обнаружить какую-то структурную ошибку в моей хранимой процедуре. Знакомства ЛПП:

CREATE procedure [dbo].[BOB] (@output_id int OUTPUT, @output_msg varchar(255) OUTPUT) 
as 
BEGIN 
SET NOCOUNT ON 
DECLARE @id int 
DECLARE @record_id int 

SET @output_id = 1 

-- some preliminary if-statements that doesn't alter any data, but might do a RETURN 

SET XACT_ABORT ON 
BEGIN TRANSACTION 

BEGIN TRY 
    --insert into table A 

    SET @id = SCOPE_IDENTITY() 

    --update table B 

    DECLARE csr cursor local FOR 
     SELECT [some stuff] and record_id 
     FROM temp_table_that_is_not_actually_a_temporary_table 
    open csr 

    fetch next from csr into [some variables], @record_id 
    while @@fetch_status=0 
    begin 
     --check type of item + if valid 
     IF (something) 
     BEGIN 
      SET SOME VARIABLE 
     END 
     ELSE 
     BEGIN 
      ROLLBACK TRANSACTION 
      SET @output_msg = 'item does not exist' 
      SET @output_id = 0 
      RETURN 
     END 

     --update table C 

     --update table D 

     --insert into table E 

     --execute some other stored procedure (without transactions) 

     if (something) 
     begin 
      --insert into table F 

      --update table C again  
     end 

     DELETE FROM temp_table_that_is_not_actually_a_temporary_table WHERE [email protected]_id 

     fetch next from csr into [some variables], @record_id 
    end 
    close csr 
    deallocate csr 

    COMMIT TRANSACTION 
    SET @output_msg = 'ok' 
    SET @output_id = @id 
END TRY 
BEGIN CATCH 
    ROLLBACK TRANSACTION 
    SET @output_msg = 'transaction failed !' 
    SET @output_id = 0 
    INSERT INTO errors (record_time, sp_name, sp_msg, error_msg) 
    VALUES (getdate(), 'BOB', @output_msg, error_message()) 
END CATCH 

RETURN 
END 

Я знаю, мой пользователь получает @output_id, который в SCOPE_IDENTITY(), и он также получает @output_msg, который говорит «ОК». Есть ли способ получить эти результаты без транзакции?

спасибо.

+0

Я не вижу ничего в процедуре, что вызвало бы поведение вы говорите, что происходит. Однако я бы предположил, что использование такого курсора не является наиболее эффективным способом выполнения этого типа вещей. Было бы лучше с нескольких точек зрения изменить это на подход, основанный на наборах, вместо RBAR. –

+0

Спасибо за ответ. Но я не думаю, что на самом деле существует подход, основанный на наборе, который будет работать в этом случае. Во всяком случае, большую часть времени таблица «temp» содержит менее пяти строк, поэтому это не имеет большого значения. –

+0

Я бы по крайней мере предложил использовать фактическую таблицу темпа вместо постоянной таблицы, действующей как временная таблица. Это проблематично, когда параллелизм вступает в игру. –

ответ

0

Лично я никогда не использовал транзакции в хранимых процедурах, особенно когда они используются одновременно многими людьми. Я серьезно избегаю курсоров.

Я думаю, что я бы пошел с передачей вовлеченных строк temp_table_that_is_not_actually_a_temporary_table в настоящую временную таблицу, а затем перешел с инструкцией if для всех строк вместе. Это так просто в tsql:

выберите (данные) в #temp из (normal_table) где (условия).

Какой смысл проверять каждую строку, выполнять задание, а затем откатывать все это, если сказать, что последняя строка не соответствует этому условию? Сделайте чек для всех сразу, выполняйте работу для всех сразу. Вот что такое sql.

+0

На самом деле я уже изменил процедуру таким образом, чтобы она передавала строки в настоящую временную таблицу. Это не решило мою проблему. –

+0

Что касается курсора: это был бы чрезвычайно сложный код, если бы мне пришлось делать это без курсора. Есть серьёзно много проверок для всех видов странных вещей в зависимости от типа элемента. Я вижу вашу точку зрения, проверяя все сначала, но я не думаю, что это имеет смысл в этом случае, и я не вижу, насколько это актуально для этой проблемы, Что касается транзакций: я не понимаю, почему вы просто избегаете этого , Как вы можете убедиться, что вы не получите половину «транзакций»? (На самом деле для этой системы важно, чтобы это либо происходило, либо нет. Все, что между ними не допускается.) –

+0

Трудно понять, что именно нужно от абстрактного примера. Я бы не ожидал, что для базы данных OLTP потребуется столько обновлений для одной транзакции. Однако, если вы можете работать асинхронно, это лучший способ сделать это. Создайте промежуточную таблицу и вместо обновления всех этих таблиц подайте данные транзакции в промежуточное. Добавьте столбец состояния «Статус». Регулярно разбирайте эту таблицу и выполняйте транзакции со статусом null. После выполнения каждой транзакции статус обновления завершается или не выполняется. –

1

Вы знаете, что проблема заключается в том, что транзакционная доза НЕ поддерживает откат по переменным, поскольку в базе данных нет данных. Либо фиксация, либо откат транзакций ТОЛЬКО имеют значение для этих объектов базы данных (таблицы, временная таблица и т. Д.), НЕ ПЕРЕМЕННЫЕ (включая переменные таблицы).

- EDIT

declare @v1 int = 0, @v2 int = 0, @v3 int = 0 

set @v2 = 1 

begin tran 
set @v1 = 1 
commit tran 

begin tran 
set @v3 = 1 
rollback tran 

select @v1 as v1, @v2 as v2, @v3 as v3 

РЕЗУЛЬТАТ выглядит следующим образом

enter image description here

+0

Спасибо, что указали это. Я на самом деле не думал об этом. Однако значение переменной не устанавливается до фиксации. Кроме того, я не упомянул в своем вопросе, что после возвращения пользователя @output_id пользователю пользователь извлекает данные из таблицы на основе идентификатора, который он делает успешно. Но все же, спустя некоторое время, данных нет. –

+0

Когда значение переменной задано, зависит от запроса, а не от фиксации транзакции или откат. Я добавил несколько запросов и результатов, чтобы показать их вам. Надеюсь, поможет. :) –

+0

Да, я понимаю, что вы имеете в виду. Но в моем случае это больше похоже на это: declare @v int; начать движение; - добавить строку в таблицу; совершить переход; set @v = 1; –