Insert-exec blokları adı verilen saklı yordamlardaki özel durumları işleme


10

Ekleme-exec bloğunda çağrılan bir saklı yordam var:

insert into @t
    exec('test')

Saklı yordamda oluşturulan özel durumları nasıl işleyebilir ve yine de işlemeye devam edebilirim?

Aşağıdaki kod sorunu göstermektedir. Ne yapmak istiyorum dahili exec()aramanın başarılı veya başarısız bağlı olarak 0 veya -1 döndürmek :

alter procedure test -- or create
as
begin try
    declare @retval int;
    -- This code assumes that PrintMax exists already so this generates an error
    exec('create procedure PrintMax as begin print ''hello world'' end;')
    set @retval = 0;
    select @retval;
    return(@retval);
end try
begin catch
    -- if @@TRANCOUNT > 0 commit;
    print ERROR_MESSAGE();
    set @retval = -1;
    select @retval;
    return(@retval);
end catch;
go

declare @t table (i int);

insert into @t
    exec('test');

select *
from @t;

Benim sorunum return(-1). Başarı yolu iyi.

Saklı yordamda try / catch bloğunu dışarıda bırakırsam hata oluşur ve kesici uç başarısız olur. Ancak, ne yapmak istiyorum hatayı işlemek ve güzel bir değer döndürmektir.

Olduğu gibi kod şu iletiyi döndürür:

Msg 3930, Level 16, State 1, Line 6
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.

Bu belki de karşılaştığım en kötü hata mesajı. Gerçekten de "İç içe geçmiş bir işlemde hata işlemediniz" anlamına geliyor.

Eğer koyarsam if @@TRANCOUNT > 0, mesajı alırım:

Msg 3916, Level 16, State 0, Procedure gordontest, Line 7
Cannot use the COMMIT statement within an INSERT-EXEC statement unless BEGIN TRANSACTION is used first.

Başlangıç ​​/ taahhüt işlem ifadeleri ile oynamayı denedim, ancak hiçbir şey işe yaramadı.

Öyleyse, saklı yordamın genel işlemi iptal etmeden hataları işlemesini nasıl sağlayabilirim?

Martin'e yanıt olarak düzenleyin:

Gerçek arama kodu:

        declare @RetvalTable table (retval int);

        set @retval = -1;

        insert into @RetvalTable
            exec('

@retval int bildir; exec @retval = '+ @ sorgu +'; @retval 'seçin);

        select @retval = retval from @RetvalTable;

@querySaklı yordam çağrısı nerede . Amaç, saklanan yordamdan dönüş değerini elde etmektir. Bu, insert(veya daha spesifik olarak, bir işlem başlatmadan) mümkünse , bu harika olurdu.

Saklı yordamları genel olarak değeri bir tabloda saklamak için değiştiremiyorum, çünkü çok fazla var. Bunlardan biri başarısız oluyor ve bunu değiştirebilirim. Benim mevcut en iyi çözüm gibi bir şey:

if (@StoredProcedure = 'sp_rep__post') -- causing me a problem
begin
    exec @retval = sp_rep__post;
end;
else
begin
    -- the code I'm using now
end;

Tablo değişkenine ne eklemeye çalışıyorsunuz? Dönüş değeri yine de oraya eklenmez. declare @t table (i int);declare @RC int;exec @RC = test;insert into @t values (@RC);select * from @t;iyi çalışıyor.
Martin Smith

@MartinSmith. . . Kodun gerçekte nasıl çalıştığı, select @retval; return @retvalsondaki gibidir. Dinamik saklı yordam çağrısından dönüş değerini almanın başka bir yolunu biliyorsanız, bilmek isterim.
Gordon Linoff

Başka bir yol olurduDECLARE @RC INT;EXEC sp_executesql N'EXEC @RC = test', N'@RC INT OUTPUT', @RC = @RC OUTPUT;insert into @t VALUES (@RC)
Martin Smith

@MartinSmith. . . Bunun işe yarayacağını düşünüyorum. Gününüzü donanım hatası ("günlük dosyasına yazılan işlemleri destekleyemiyor" bir donanım hatası gibi) arayarak geçirdik ve son birkaç saat içinde kodu doğru bulmaya çalışıyoruz. Değişken değiştirme mükemmel bir cevaptır.
Gordon Linoff

Yanıtlar:


13

İfadenin bir EXECkısmındaki hata INSERT-EXEC, işleminizi mahkum bir durumda bırakıyor.

Size Eğer PRINTdışarı XACT_STATE()içinde CATCHo ayarlandığında blokta -1.

Tüm hatalar durumu buna ayarlamaz. Aşağıdaki kontrol kısıtlama hatası catch bloğuna gider ve INSERTbaşarılı olur.

ALTER PROCEDURE test -- or create
AS
  BEGIN try
      DECLARE @retval INT;

      DECLARE @t TABLE(x INT CHECK (x = 0))

      INSERT INTO @t
      VALUES      (1)

      SET @retval = 0;

      SELECT @retval;

      RETURN( @retval );
  END try

  BEGIN catch
      PRINT XACT_STATE()

      PRINT ERROR_MESSAGE();

      SET @retval = -1;

      SELECT @retval;

      RETURN( @retval );
  END catch; 

Bunu CATCHbloğa ekleme

 IF (XACT_STATE()) = -1
BEGIN
    ROLLBACK TRANSACTION;
END;

Yardımcı olmuyor. Hata veriyor

INSERT-EXEC deyimi içinde ROLLBACK deyimi kullanılamaz.

Bir kez meydana geldiğinde böyle bir hatayı düzeltmenin herhangi bir yolu olduğunu sanmıyorum. Özel kullanım durumunuz için INSERT ... EXECyine de ihtiyacınız yok . Dönüş değerini bir skaler değişkene atayabilir ve bunu ayrı bir ifadeye ekleyebilirsiniz.

DECLARE @RC INT;

EXEC sp_executesql
  N'EXEC @RC = test',
  N'@RC INT OUTPUT',
  @RC = @RC OUTPUT;

INSERT INTO @t
VALUES      (@RC) 

Veya elbette, bu saklı yordamı, bu hatayı hiç yükseltmeyecek şekilde yeniden yapılandırabilirsiniz.

DECLARE @RetVal INT = -1

IF OBJECT_ID('PrintMax', 'P') IS NULL
  BEGIN
      EXEC('create procedure PrintMax as begin print ''hello world'' end;')

      SET @RetVal = 0
  END

SELECT @RetVal;

RETURN( @RetVal ); 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.