SQL Server'da aynı özel durum nasıl yeniden atılır


86

Try bloğumda az önce oluşan aynı istisnayı SQL Server'da yeniden atmak istiyorum. Aynı mesajı atabiliyorum ama aynı hatayı atmak istiyorum.

BEGIN TRANSACTION
    BEGIN TRY
        INSERT INTO Tags.tblDomain (DomainName, SubDomainId, DomainCode, Description)
            VALUES(@DomainName, @SubDomainId, @DomainCode, @Description)
        COMMIT TRANSACTION
    END TRY
    
    BEGIN CATCH
        declare @severity int; 
        declare @state int;

        select @severity=error_severity(), @state=error_state();

        RAISERROR(@@Error,@ErrorSeverity,@state);
        ROLLBACK TRANSACTION
    END CATCH

RAISERROR(@@Error, @ErrorSeverity, @state);

Bu satır hata gösterecek, ancak bunun gibi bir işlevsellik istiyorum. Bu, 50000 hata numaralı hataya neden olur, ancak geçtiğim hata numarasının atılmasını istiyorum@@error ,

Bu hatayı ön uçta yakalamak istiyorum.

yani

catch (SqlException ex)
{
    if ex.number==2627
    MessageBox.show("Duplicate value cannot be inserted");
}

Bu işlevi istiyorum. hangi kullanılarak elde edilemez raiseerror. Arka uçta özel hata mesajı vermek istemiyorum.

RAISEERROR catch'te atılacak ErrorNo'yu geçtiğimde aşağıda belirtilen hatayı döndürmeli

Msg 2627, Level 14, State 1, Procedure spOTest_DomainInsert,

Satır 14 UNIQUE KEY kısıtlaması 'UK_DomainCode' ihlali. Yinelenen anahtar 'Tags.tblDomain' nesnesine eklenemez. Açıklama sona erdirildi.

DÜZENLE:

Saklı yordamın yürütülmesi gereken birden çok sorgu içerdiğini düşünerek istisnanın ön uçta işlenmesini istiyorsam, try catch bloğunu kullanmamanın dezavantajı ne olabilir?

Yanıtlar:


120

Burada, bir hata oluşursa ve hata mesajını bildirirse bir dizi ifadeyi geri almak için tamamen işlevsel bir temiz kod örneği verilmiştir.

begin try
    begin transaction;

    ...

    commit transaction;
end try
begin catch
    if @@trancount > 0 rollback transaction;
    throw;
end catch

SQL 2012'den önce

begin try
    begin transaction;
    
    ...
    
    commit transaction;
end try
begin catch
    declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;
    select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
    if @@trancount > 0 rollback transaction;
    raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);
end catch

8
Bunu bir saklı yordamın ortasında kullanıyordum ve daha sonra çalışmaya devam edeceğini fark ettim raiserror, bu, c # 'nin a throw. Bu yüzden bu davranışı eşleştirmek istediğim için returniçine bir ekledim catch.
Brian J

@BogdanBogdanov Düzenlemenizi geri aldım çünkü bu kodun amacı çok az olmalı ve kodun yerine girilen gerçek koddan
uzaklaşmamalı

Tamam, sorun değil @Ben Gripka. Ekranda daha okunabilir hale getirmeye çalışıyorum. Geri alma işleminin nedenini gösterdiğin için teşekkürler.
Bogdan Bogdanov

1
@BrianJ: normalde, yürütmenin durup durmayacağı orijinal hatanın ciddiyetine bağlıdır. Önem derecesi> = 11 ise, yürütme durmalıdır. Bu gerçekten tuhaf çünkü catch bloğunun içindeki> = 11 şiddeti ile yükselen hata artık yürütmeyi durdurmuyor. Gözleminiz çok iyi ve en azından 2008r2 gibi bir sql sunucusunun ne kadar beyin ölümcül olduğunu gösteriyor. Yeni sürümler daha iyi görünüyor.
costa

1
@costa RAISERROR()'ın belgelerine bakın . ≥11'in şiddeti, yalnızca bir CATCHbloğun içindeyse bir bloğa atlar TRY. Yani gerekir var BEGIN TRY…END CATCHsen senin istiyorsanız kodunu etrafında RAISERROR()akış kontrol etkileyecek.
binki

137

SQL 2012, throw ifadesini sunar:

http://msdn.microsoft.com/en-us/library/ee677615.aspx

THROW ifadesi parametreler olmadan belirtilirse, bir CATCH bloğunun içinde görünmesi gerekir. Bu, yakalanan istisnanın ortaya çıkmasına neden olur.

BEGIN TRY
    BEGIN TRANSACTION
    ...
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    THROW
END CATCH

2
Dikkat edin, bu çözüm yalnızca 2012 ve üzeri sql sunucusunda çalışıyor gibi görünüyor: msdn.microsoft.com/en-us/library/ee677615.aspx
Adi

3
@BogdanBogdanov Hatayı günlüğe kaydedebilmeniz, muhtemelen bazı durumların üstesinden gelebilmeniz için, ancak yapamıyorsanız, hatayı yeniden atmak istersiniz, böylece daha yüksek bir dene / yakala, daha sonra başa çıkma şansına sahip olabilir
Robert McKee

Evet, @Robert McKee. Ben anladım. Üzgünüm, bu yorumu silmeyi unuttu.
Bogdan Bogdanov

4
ROLLBACKSatırın üzerindeki bu noktalı virgül önemlidir! Onsuz bir SQLException: Cannot roll back THROW.
idontevenseethecode

5

CATCH bloğunun içine yeniden atma (SQL2012 öncesi kod, SQL2012 ve sonrası için THROW deyimini kullanın):

DECLARE
    @ErrorMessage nvarchar(4000) = ERROR_MESSAGE(),
    @ErrorNumber int = ERROR_NUMBER(),
    @ErrorSeverity int = ERROR_SEVERITY(),
    @ErrorState int = ERROR_STATE(),
    @ErrorLine int = ERROR_LINE(),
    @ErrorProcedure nvarchar(200) = ISNULL(ERROR_PROCEDURE(), '-');
SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + @ErrorMessage;
RAISERROR (@ErrorMessage, @ErrorSeverity, 1, @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine)

4

Sanırım seçimleriniz:

  • Hatayı yakalama (kabarmasına izin ver)
  • Özel bir tane yükseltin

Bir noktada, SQL muhtemelen bir yeniden yükseltme komutu veya yalnızca belirli hataları yakalama yeteneği sunacaktır. Ancak şimdilik bir geçici çözüm kullanın. Afedersiniz.


7
sql 2012'de yeni THROW anahtar sözcüğünü kullanarak bir istisnayı yeniden yükseltebilirsiniz
sergiom

5
Evet. Elbette, bu soru sorulduğunda bu mevcut değildi.
Rob Farley

Yeni bir hatayı yakalayıp fırlatmak, onu yakalamamaktan ve “kabarmasına” izin vermekten daha önemli olacaktır çünkü istisnayı doğru bir şekilde ele almak için muhtemelen bazı temizleme, düzeltici eylem ve kapatma faaliyetlerine ihtiyacınız olacaktır. Açık bir örnek, bir imleci kapatıp imha etmek olabilir. Diğer örnekler, bir günlük kaydı prosedürünü yürütmek veya bazı verileri sıfırlamak olabilir.
Antony Booth

1

Yapamazsınız: yalnızca motor 50000'den az hata atabilir. Yapabileceğiniz tek şey görünen bir istisna atmaktır. benzeyen ...

Cevabımı burada görün lütfen

Buradaki sorgulayıcı, istediği şeyi yapmak için müşteri tarafı işlemlerini kullandı ki bu biraz aptalca ...


0

Tamam, bu geçici bir çözüm ... :-)

DECLARE @Error_Number INT
BEGIN TRANSACTION 
    BEGIN TRY
    INSERT INTO Test(Id, Name) VALUES (newID(),'Ashish') 
    /* Column 'Name' has unique constraint on it*/
    END TRY
    BEGIN CATCH

            SELECT ERROR_NUMBER()
            --RAISERROR (@ErrorMessage,@Severity,@State)
            ROLLBACK TRAN
    END CATCH

Yakalama bloğunu not ederseniz, bu, hatayı yükseltmez, ancak gerçek hata numarasını döndürür (ve ayrıca işlemi geri alır). Şimdi .NET kodunuzda, istisnayı yakalamak yerine, ExecuteScalar () kullanırsanız, istediğiniz gerçek hata numarasını alır ve uygun numarayı gösterirsiniz.

int errorNumber=(int)command.ExecuteScalar();
if(errorNumber=<SomeNumber>)
{
    MessageBox.Show("Some message");
}

Bu yardımcı olur umarım,

DÜZENLEME: - Sadece bir not, Etkilenen kayıtların sayısını öğrenmek ve ExecuteNonQuery'yi kullanmak istiyorsanız, yukarıdaki çözüm sizin için çalışmayabilir. Aksi takdirde, ihtiyacın olana uyacağını düşünüyorum. Bilmeme izin ver.


@Ashish Gupta: Yardım için Thx, Ama ihtiyaç istisna aksi ı u önerdi birçok seçenek baskı ERROR_NUMBER (), dönüş ERROR_NUMBER ve 1 gibi açmasını, önyüz için veritabanından atılmasını
Shantanu Gupta

0

Bir hata oluştuktan sonra saklı yordamda yürütmeyi durdurmanın ve hatayı çağıran programa geri döndürmenin yolu, bu kodla bir hata oluşturabilecek her bir ifadeyi izlemektir:

If @@ERROR > 0
Return

Depolanan bir yordamdaki yürütmenin bir hatadan sonra devam edebileceğini öğrendiğimde kendimi şaşırdım - bunun farkında olmamak bazı hataları izlemeyi zorlaştırabilir.

Bu tür bir hata işleme türü paraleldir (.Net öncesi) Visual Basic 6. SQL Server 2012'de Throw komutunu bekliyoruz.


0

Henüz 2012'ye taşınmadığınız göz önüne alındığında, orijinal hata kodunun köpürmesini uygulamanın bir yolu, catch bloğundan (yeniden) attığınız istisnanın metin mesajı kısmını kullanmaktır. Arayan kodunuzun catch bloğunda ayrıştırılması için XML metni gibi bir yapı içerebileceğini unutmayın.


0

SQL deyiminin işlem içinde yürütülmesini istediğinizde bu senaryolar için bir sarmalayıcı saklı yordamı oluşturabilir ve hatayı kodunuza kadar besleyebilirsiniz.

CREATE PROCEDURE usp_Execute_SQL_Within_Transaction
(
    @SQL nvarchar(max)
)
AS

SET NOCOUNT ON

BEGIN TRY
    BEGIN TRANSACTION
        EXEC(@SQL)
    COMMIT TRANSACTION
END TRY

BEGIN CATCH
    DECLARE @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int
    SELECT @ErrorMessage = N'Error Number: ' + CONVERT(nvarchar(5), ERROR_NUMBER()) + N'. ' + ERROR_MESSAGE() + ' Line ' + CONVERT(nvarchar(5), ERROR_LINE()), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE()
    ROLLBACK TRANSACTION
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState)
END CATCH

GO

-- Test it
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1/0; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'EXEC usp_Another_SP'

-2

Tasarım açısından bakıldığında, orijinal hata numaraları ve özel mesajlarla istisnalar atmanın amacı nedir? Bir dereceye kadar, uygulamalar ve veritabanı arasındaki arayüz sözleşmesini bozar. Orijinal hataları yakalamak ve daha yüksek kodda işlemek istiyorsanız, bunları veritabanında işlemeyin. Daha sonra bir istisna yakaladığınızda, kullanıcıya sunulan mesajı istediğiniz herhangi bir şeye değiştirebilirsiniz. Bunu yapmam, çünkü veritabanı kodunuzu hmm 'doğru değil' yapar. Diğerlerinin dediği gibi, kendi hata kodlarınızdan oluşan bir set (50000'ün üzerinde) tanımlamanız ve yerine bunları atmanız gerekir. Daha sonra, bütünlük sorunlarını ('Yinelenen değerlere izin verilmez') potansiyel iş sorunlarından ayrı olarak halledebilirsiniz - 'Posta kodu geçersiz', 'Kriterlerle eşleşen satır bulunamadı' vb.


9
Orijinal hata numaraları ve özel mesajlarla istisnalar atmanın amacı nedir? Bir veya iki belirli (beklenen) hatayı doğrudan catch bloğunda işlemek ve gerisini daha yüksek katmanlara bırakmak istediğinizi varsayalım. Bu nedenle, ele almadığınız istisnaları yeniden atabilmeniz gerekir ... tercihen hataları bildirmek ve başka bir özel yolla ele almak zorunda kalmadan.
Jenda

1
@ Jenda'nın açıkladığına ek olarak, kod yürütmenin bir istisnadan sonra devam etmediğinden emin olmak için try-catch'i kullanmayı seviyorum, C #: try { code(); } catch (Exception exc) { log(exc); throw; } finally { cleanup(); }'da olduğu gibi throw;, sadece orijinal istisnayı orijinal bağlamıyla ortaya çıkaracak.
R.Schreurs

Hataları yakaladım ve hatanın hangi satırda gerçekleştiğini açıklayan ayrıntıları veya hatayı daha sonra izlememe yardımcı olacak diğer ayrıntıları (eklenmeye çalışan veriler gibi) eklemek için SQL'de özel hata mesajlarını yeniden atıyorum.
Russell Hankins
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.