Bir SQL Server kullanıcı tanımlı işlevinden bir hata nasıl bildirilir


149

SQL Server 2008'de kullanıcı tanımlı bir işlev yazıyorum. İşlevlerin her zamanki şekilde hata üretemeyeceğini biliyorum - RAISERROR deyimini eklemeye çalışırsanız SQL şunu döndürür:

Msg 443, Level 16, State 14, Procedure ..., Line ...
Invalid use of a side-effecting operator 'RAISERROR' within a function.

Ancak gerçek şu ki, işlev geçersiz olabilecek bazı girdiler alır ve eğer öyleyse işlevin döndürebileceği anlamlı bir değer yoktur. O zaman ne yapacağım

Elbette NULL döndürebilirim, ancak işlevi kullanan herhangi bir geliştiricinin bu sorunu gidermesi zor olurdu. Ayrıca sıfıra bölünmeye veya bunun gibi bir şeye neden olabilirim - bu bir hata mesajı oluşturur, ancak yanıltıcıdır. Kendi hata mesajımı bir şekilde bildirebilmemin bir yolu var mı?

Yanıtlar:


227

Anlamlı bir hata atmak için CAST'ı kullanabilirsiniz:

create function dbo.throwError()
returns nvarchar(max)
as
begin
    return cast('Error happened here.' as int);
end

Ardından Sql Server bazı yardım bilgilerini gösterecektir:

Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'Error happened here.' to data type int.

117
Harika cevap, ama JEEZ wotta hack. > :(
JohnL4

5
RETURN'ün basit bir seçim olduğu bir satır içi tablo değerli işlev için, bu tek başına çalışmaz çünkü hiçbir şey döndürülmez - boş bile değil ve benim durumumda hiçbir şey bulunmadığında bir hata atmak istedim. Açık performans nedenlerinden ötürü satır içi işlevi çoklu ifadeye bölmek istemedim. Bunun yerine çözümünüzü artı ISNULL ve MAX kullandım. RETURN ifadesi artık şu şekilde görünür: SELECT ISNULL (MAX (E.EntityID), CAST ('The Lookup (' + @LookupVariable + ') mevcut değil.' Int olarak)) [EntityID] FROM Entity as E WHERE E. Arama = @ LookupVariable
MikeTeeVee

Evet, bir hata atabilirsiniz, ancak şartlı olarak bir hata atabileceğiniz görülmüyor. İşlev, kod yolundan bağımsız olarak yürütülür.
satnhak

10
Harika bir çözüm, ancak TVF kullananlar için bu kolayca geri dönüşün bir parçası olamaz. Bunlar için:declare @error int; set @error = 'Error happened here.';
Tim Lehner

22
Binlerce yanan güneşin gücünden nefret ediyorum. Başka seçenek yok mu? İnce. Ama cripes ...
Remi Despres-Smyth

18

Genel numara, 0'a bölmeye zorlamaktır. Bu, bir hataya neden olur ve işlevi değerlendiren geçerli ifadeyi kesintiye uğratır. Geliştirici veya destek kişisi bu davranışı biliyorsa, sorunu araştırmak ve gidermek oldukça kolaydır, çünkü 0 hatasına bölme farklı, ilgisiz bir sorunun belirtisi olarak anlaşılır.

Bu, herhangi bir açıdan göründüğü kadar kötü görünse de, maalesef şu anda SQL işlevlerinin tasarımı daha iyi bir seçeneğe izin vermiyor. RAISERROR kullanımına işlevlerde kesinlikle izin verilmelidir.


7

Vladimir Korolev'in cevabından yola çıkarak, koşullu olarak hata atma deyimi şudur:

CREATE FUNCTION [dbo].[Throw]
(
    @error NVARCHAR(MAX)
)
RETURNS BIT
AS
BEGIN
    RETURN CAST(@error AS INT)
END
GO

DECLARE @error NVARCHAR(MAX)
DECLARE @bit BIT

IF `error condition` SET @error = 'My Error'
ELSE SET @error = '0'

SET @bit = [dbo].[Throw](@error)    

6

Bence en temiz yol, geçersiz argümanlar iletilirse işlevin NULL döndürebileceğini kabul etmektir. Bu açıkça belgelendiği sürece sorun olmaz mı?

-- =============================================
-- Author: AM
-- Create date: 03/02/2010
-- Description: Returns the appropriate exchange rate
-- based on the input parameters.
-- If the rate cannot be found, returns NULL
-- (RAISEERROR can't be used in UDFs)
-- =============================================
ALTER FUNCTION [dbo].[GetExchangeRate] 
(
    @CurrencyFrom char(3),
    @CurrencyTo char(3),
    @OnDate date
)
RETURNS decimal(18,4)
AS
BEGIN

  DECLARE @ClosingRate as decimal(18,4)

    SELECT TOP 1
        @ClosingRate=ClosingRate
    FROM
        [FactCurrencyRate]
    WHERE
        FromCurrencyCode=@CurrencyFrom AND
        ToCurrencyCode=@CurrencyTo AND
        DateID=dbo.DateToIntegerKey(@OnDate)

    RETURN @ClosingRate 

END
GO

5

RAISEERRORveya @@ERRORUDF'lerde izin verilmez. UDF'yi zorlu bir prosedüre dönüştürebilir misiniz?

Erland Sommarskog'un SQL Server'da Hata İşleme - Bir Arka Plan makalesinden :

Kullanıcı tanımlı işlevler genellikle SET, SELECT, INSERT, UPDATE veya DELETE deyiminin bir parçası olarak çağrılır. Bulduğum şey, çok ifadeli tablo değerli bir işlevde veya skaler bir işlevde bir hata ortaya çıkarsa, işlevin yürütülmesinin derhal durdurulduğu ve işlevin parçası olduğu ifade de budur. Hata toplu işi iptal etmedikçe, yürütme sonraki satırda devam eder. Her iki durumda da @@ hatası 0'dır. Dolayısıyla, T-SQL'den bir işlevde bir hata oluştuğunu tespit etmenin bir yolu yoktur.

Satır içi tablo değerli bir işlev temelde sorgu işlemcisinin sorguya yapıştırdığı bir makro olduğundan, sorun satır içi tablo işlevlerinde ortaya çıkmaz.

EXEC deyimiyle skaler işlevleri de çalıştırabilirsiniz. Bu durumda, bir hata meydana gelirse yürütme devam eder (bu bir toplu iptal hatası olmadığı sürece). @@ hatası ayarlanmıştır ve işlev içinde @@ hatasının değerini kontrol edebilirsiniz. Yine de, hatayı arayana bildirmek sorunlu olabilir.


4

En iyi yanıt genellikle en iyisidir, ancak satır içi tablo değerli işlevler için çalışmaz.

MikeTeeVee, en üstteki yanıtla ilgili yorumunda bunun için bir çözüm sundu, ancak bu, benim durumum için iyi çalışmayan MAX gibi bir toplama işlevinin kullanılmasını gerektirdi.

Bir toplama yerine select * gibi bir şey döndüren satır içi tablo değerli bir udf'ye ihtiyaç duyduğunuz durumda alternatif bir çözüm buldum . Bu özel durumu çözen örnek kod aşağıdadır. Birisinin daha önce de belirttiği gibi ... "JEEZ wotta hack" :) Bu durum için daha iyi bir çözümü memnuniyetle karşılıyorum!

create table foo (
    ID nvarchar(255),
    Data nvarchar(255)
)
go

insert into foo (ID, Data) values ('Green Eggs', 'Ham')
go

create function dbo.GetFoo(@aID nvarchar(255)) returns table as return (
    select *, 0 as CausesError from foo where ID = @aID

    --error checking code is embedded within this union
    --when the ID exists, this second selection is empty due to where clause at end
    --when ID doesn't exist, invalid cast with case statement conditionally causes an error
    --case statement is very hack-y, but this was the only way I could get the code to compile
    --for an inline TVF
    --simpler approaches were caught at compile time by SQL Server
    union

    select top 1 *, case
                        when ((select top 1 ID from foo where ID = @aID) = @aID) then 0
                        else 'Error in GetFoo() - ID "' + IsNull(@aID, 'null') + '" does not exist'
                    end
    from foo where (not exists (select ID from foo where ID = @aID))
)
go

--this does not cause an error
select * from dbo.GetFoo('Green Eggs')
go

--this does cause an error
select * from dbo.GetFoo('Yellow Eggs')
go

drop function dbo.GetFoo
go

drop table foo
go

1
okuyan biri için, potansiyel performans etkilerine
bakmadım

4

" RETURN [geçersiz çevrim] " gibi şeyler kullanamayacağınız için, birkaç kişi Tablo Değerli işlevlerde hataları yükseltmek hakkında sorular soruyordu . Geçersiz atamanın bir değişkene atanması da aynı şekilde çalışır.

CREATE FUNCTION fn()
RETURNS @T TABLE (Col CHAR)  
AS
BEGIN

DECLARE @i INT = CAST('booooom!' AS INT)  

RETURN

END

Bunun sonucu:

Msg 245, Düzey 16, Durum 1, Satır 14 Varchar değeri 'booooom!' Dönüştürülürken dönüştürme başarısız oldu veri türü int.


2

Davec'in tablo değerli işlevle ilgili cevabının altında yorum yapamam, ancak benim mütevazi görüşüme göre bu daha kolay bir çözüm:

CREATE FUNCTION dbo.ufn_test (@a TINYINT)
RETURNS @returns TABLE(Column1 VARCHAR(10), Value1 TINYINT)
BEGIN
    IF @a>50 -- if @a > 50 - raise an error
    BEGIN
      INSERT INTO @returns (Column1, Value1)
      VALUES('error','@a is bigger than 50!') -- reminder Value1 should be TINYINT
    END

    INSERT INTO @returns (Column1, Value1)
    VALUES('Something',@a)
    RETURN;
END

SELECT Column1, Value1 FROM dbo.ufn_test(1) -- this is okay
SELECT Column1, Value1 FROM dbo.ufn_test(51) -- this will raise an error

-3

Bir yol (hack), geçersiz bir eylem gerçekleştiren bir işleve / depolanmış prosedüre sahip olmaktır. Örneğin, aşağıdaki sözde SQL

create procedure throw_error ( in err_msg varchar(255))
begin
insert into tbl_throw_error (id, msg) values (null, err_msg);
insert into tbl_throw_error (id, msg) values (null, err_msg);
end;

Tbl_throw_error tablosunda, err_msg sütununda benzersiz bir sınırlama vardır. Bunun bir yan etkisi (en azından MySQL'de), err_msg değerinin, uygulama seviyesi istisna nesnesine geri döndüğünde istisnanın açıklaması olarak kullanılmasıdır.

SQL Server ile benzer bir şey yapıp yapamayacağınızı bilmiyorum ama denemeye değer.


5
İlginç bir fikir, ancak INSERT işlevine de izin verilmez.
EMP
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.