2014-10-02 2 views
0

Я довольно новичок в T-SQL, и у меня проблемы с огромными сценариями с транзакциями, курсорами и процедурами хранения. Итак, мой код что-то вроде этого (этот код просто примера структуры моих сценариев, на самом деле у меня есть кратные процедуры внутри OuterProc курсора и нескольких операций внутри InnerProc курсора):Откат транзакции внутри курсоров и внутренних транзакций

create proc InnerProc 
as 
begin 
    declare @Id int 

    begin tran 

    declare mycursor cursor local static read_only forward_only 
    for select Id 
     from MyOtherTable 

    open mycursor 
    fetch next from mycursor into @Id 

    while @@fetch_status = 0 
    begin 
    select 1/0 

    if @@ERROR <> 0 
    begin 
     rollback tran 
     return @@ERROR 
    end   

    fetch next from mycursor into @Id 
    end 

    close mycursor 
    deallocate mycursor 

    commit tran 
end 


create proc OuterProc 
as 
begin 

    declare @Id int 

    begin tran 

    declare mycursor cursor local static read_only forward_only 
    for select Id 
     from MyTable 

    open mycursor 
    fetch next from mycursor into @Id 

    while @@fetch_status = 0 
    begin 
    exec @error = InnerProc 

    if @@ERROR <> 0 
    begin 
     rollback tran 
     return 
    end 
    else 
     commit tran 

    fetch next from mycursor into @Id 
    end 

    close mycursor 
    deallocate mycursor 
end 

С этим структура У меня есть эта ошибка:

Msg 515, Level 16, State 2, Procedure InnerProc, Line 448 
    Cannot insert the value NULL into column 'InitialQuantity', table 'MySecondTable'; column does not allow nulls. INSERT fails. 
    The statement has been terminated. 
Msg 266, Level 16, State 2, Procedure InnerProc, Line 0 
    Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0. 
Msg 3903, Level 16, State 1, Procedure CreateSASEExtraction, Line 79 
    The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION. 

Что не так с моим кодом? Если что-то пойдет не так внутри innerProc, я хочу, чтобы все операции для этого внешнего откат курсора и остановки внутреннего курсора. Если что-то пошло не так в outerProc, я хочу, чтобы все операции для этого курсора были отменены, но я хочу, чтобы этот курсор продолжал цикл ...
Есть лучший способ сделать это?

UPDATE:

После того как я исправить некоторые ошибки @Bernd Linde обнаружены, добавить примерочный улов в InnerProc и я назвал сделку InnerProc. Теперь у меня есть этот код:

create proc InnerProc 
as 
begin 
    declare @Id int 

    begin tran 

    begin try 

    declare mycursor cursor local static read_only forward_only 
    for select Id 
     from MyOtherTable 

    open mycursor 
    fetch next from mycursor into @Id 

    while @@fetch_status = 0 
    begin 
     select 1/0 

     if @@ERROR <> 0 
     return @@ERROR  

     fetch next from mycursor into @Id 
    end 

    close mycursor 
    deallocate mycursor 

    commit tran 
    return 0 

    end try 
    begin catch 

    return @@ERROR 

    end catch 

end 


create proc OuterProc 
as 
begin 

    declare @Id int 

    declare mycursor cursor local static read_only forward_only 
    for select Id 
     from MyTable 

    open mycursor 
    fetch next from mycursor into @Id 

    while @@fetch_status = 0 
    begin 

    begin tran 

    exec @error = InnerProc 

    if @@ERROR <> 0 
    begin 
     rollback tran 
     return 
    end 
    else 
     commit tran 

    fetch next from mycursor into @Id 
    end 

    close mycursor 
    deallocate mycursor 
end 

Но теперь у меня есть другое сообщение об ошибке:

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

Как я могу решить эту проблему?

+1

почему вы используете курсор на всех? – HLGEM

+0

@HLGEM, потому что мне нужно выполнить некоторые операции для каждой строки определенной таблицы. – Ninita

+1

Это не требует указателя. Курсоры - убийца производительности и должны быть в крайнем случае. Никто, кроме администратора баз данных с опытом работы не менее 10 лет, даже не должен использовать их вообще. – HLGEM

ответ

0

После многих попыток, наконец, я получу его.

У InnerProc должны быть только COMMIT, и OuterProc будет отвечать за откат. Для этого, когда InnerProc вызывает некоторую ошибку, которая должна быть уловлена ​​в OuterProc и вынуждена действовать как исключение. Как я хочу продолжить цикл в OuterProc, эта процедура должна иметь try-catch, где цикл принудительно и откат выполняется.

Для лучшего контроля количества транзакций я использовал @@ TRANCOUNT.

Так что решить проблему с этим кодом:

create proc InnerProc 
as 
begin 
    declare @Id int 

    begin try 

    begin tran 

    declare mycursor cursor local static read_only forward_only 
    for select Id 
     from MyOtherTable 

    open mycursor 
    fetch next from mycursor into @Id 

    while @@fetch_status = 0 
    begin 
     select 1/0 

     IF @@ERROR <> 0 
     begin 

     if @@TRANCOUNT > 0 
      rollback tran 

     close mycursor 
     deallocate mycursor 
     return @@ERROR 

     end 

     fetch next from mycursor into @Id 

    end 

    close mycursor 
    deallocate mycursor 

    commit tran 
    return 0 

    end try 
    begin catch 

     close mycursor 
     deallocate mycursor 

     return @@ERROR 
    end catch 

end 


create proc OuterProc 
as 
begin 

    declare @Id int 

    declare mycursor cursor local static read_only forward_only 
    for select Id 
     from MyTable 

    open mycursor 
    fetch next from mycursor into @Id 

    while @@fetch_status = 0 
    begin 

    begin tran 

    begin try 

     exec @error = InnerProc 

     if @@ERROR <> 0 
      RAISERROR('Exception',1,1) 


     [email protected]@TRANCOUNT > 0 
      commit tran 

     fetch next from mycursor into @Id, @Name, @CodeDGAE, @Code, @NUIT, @Project 

    end try 
    begin catch 
     if @@TRANCOUNT > 0 
      rollback tran 
     fetch next from mycursor into @Id, @Name, @CodeDGAE, @Code, @NUIT, @Project 
    end catch 
    end 

    close mycursor 
    deallocate mycursor 
end 
1

С первого взгляда вы совершаете транзакции внутри своих циклов, но вы запускаете их только за пределами цикла.
Таким образом, каждый раз, когда цикл переходит в это вторую итерацию, он будет пытаться либо совершить или откатить транзакцию, которая не существует, следовательно, почему вы получаете ошибку "The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION."

Я хотел бы предложить чтение на сделках в SQLServer на MSDN here

+0

Это имеет смысл. В коде, который я написал InnerProc, неправильно, и у меня есть фиксация вне курсора (я уже исправил это), но в OuterProc у меня действительно есть эта ошибка. Но у меня такие же ошибки. У меня должна быть попытка, или эта структура должна работать? – Ninita

+0

У вас то же самое происходит в OutProc с границами транзакций. Если вы хотите, чтобы OuterProc никогда не переставал выполнять цикл, тогда да, вам понадобится попытка catch. Но я бы предпочел сказать, почему вы получаете ошибку «Не могу вставить значение ...». –

+0

См. Мой обновленный вопрос. Я делаю некоторые изменения, и теперь у меня есть другие ошибки. Операция InnerProc не найдена при возникновении ошибки. Я думаю, что курсор InnerProc продолжит цикл, даже если выполняется откат или возврат. – Ninita

Смежные вопросы