SQL सर्वर उपयोगकर्ता-निर्धारित फ़ंक्शन से किसी त्रुटि की रिपोर्ट कैसे करें


146

मैं SQL Server 2008 में उपयोगकर्ता-परिभाषित फ़ंक्शन लिख रहा हूं। मुझे पता है कि फ़ंक्शन सामान्य तरीके से त्रुटियां नहीं उठा सकते हैं - यदि आप RAISERROR स्टेटमेंट SQL रिटर्न को शामिल करने का प्रयास करते हैं:

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

लेकिन तथ्य यह है, फ़ंक्शन कुछ इनपुट लेता है, जो अमान्य हो सकता है और, यदि यह है, तो कोई सार्थक मूल्य नहीं है फ़ंक्शन वापस आ सकता है। तो अब मैं क्या करूँ?

मैं, निश्चित रूप से, NULL लौटा सकता हूं, लेकिन किसी भी डेवलपर के लिए इस समस्या का निवारण करने के लिए फ़ंक्शन का उपयोग करना मुश्किल होगा। मैं भी शून्य या कुछ इस तरह एक विभाजन का कारण बन सकता है - यह एक त्रुटि संदेश उत्पन्न करेगा, लेकिन एक भ्रामक। क्या कोई तरीका है जिससे मैं अपनी त्रुटि संदेश किसी भी तरह से सूचित कर सकता हूं?

जवाबों:


223

सार्थक त्रुटि फेंकने के लिए आप CAST का उपयोग कर सकते हैं:

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

तब Sql सर्वर कुछ सहायता जानकारी दिखाएगा:

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

112
शानदार जवाब, लेकिन जेईईजेड हैक होगा। > :(
जॉनएल 4

5
एक इनलाइन-टेबल-वैल्यू-फंक्शन के लिए जहां RETURN एक सरल चयन है, यह अकेले काम नहीं करता है क्योंकि कुछ भी नहीं लौटाया जाता है - अशक्त भी नहीं है, और मेरे मामले में मैं एक त्रुटि फेंकना चाहता था जब कुछ भी नहीं मिला था। मैं स्पष्ट प्रदर्शन कारणों से एक बहु-प्रतिमा में इनलाइन फ़ंक्शन को तोड़ना नहीं चाहता था। इसके बजाय मैंने आपके समाधान और ISNULL और MAX का उपयोग किया। RETURN क़ानून अब इस तरह दिखता है: Select ISNULL (MAX (E.EntityID), CAST ('The Lookup (' + @LookupVariable + ') मौजूद नहीं है।' Int। लुकअप = @ लुकअप वियरेबल
माइकेटीवी

हां, आप एक त्रुटि फेंक सकते हैं, लेकिन ऐसा नहीं लगता कि आप सशर्त रूप से एक त्रुटि फेंक सकते हैं। फ़ंक्शन कोड पथ के बावजूद निष्पादित हो जाता है।
शनि sat

10
महान समाधान, लेकिन उन लोगों के लिए जो टीवीएफ का उपयोग कर रहे हैं, यह आसानी से वापसी का हिस्सा नहीं हो सकता है। उन लोगों के लिए:declare @error int; set @error = 'Error happened here.';
टिम लेहनर

20
मैं एक हजार जलते हुए सूरज की शक्ति से इससे नफरत करता हूं। कोई अन्य विकल्प नहीं? ठीक। लेकिन cripes ...
रेमी Despres-Smyth

18

सामान्य चाल को 0. से विभाजित करने के लिए बाध्य करना है। यह एक त्रुटि को बढ़ाएगा और वर्तमान कथन को बाधित करेगा जो फ़ंक्शन का मूल्यांकन कर रहा है। यदि डेवलपर या सहायक व्यक्ति इस व्यवहार के बारे में जानता है, तो समस्या की जांच करना और उसका निवारण करना काफी आसान है क्योंकि 0 त्रुटि द्वारा विभाजन को एक अलग, असंबंधित समस्या के लक्षण के रूप में समझा जाता है।

जितना बुरा यह किसी भी दृष्टिकोण से दिखता है, दुर्भाग्य से इस समय SQL कार्यों का डिज़ाइन बेहतर विकल्प की अनुमति नहीं देता है। कार्यों में RAISERROR का उपयोग करने की अनुमति दी जानी चाहिए।


7

व्लादिमीर कोरोलेव के जवाब के बाद, मुहावरा सशर्त रूप से एक त्रुटि फेंकना है

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

मुझे लगता है कि सबसे साफ तरीका सिर्फ यह स्वीकार करना है कि यदि अमान्य तर्क पारित किए जाते हैं तो फ़ंक्शन NULL वापस कर सकता है। जब तक यह स्पष्ट रूप से प्रलेखित है तब तक यह ठीक होना चाहिए?

-- =============================================
-- 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

RAISEERRORया @@ERRORयूडीएफ में अनुमति नहीं है। क्या आप UDF को एक प्रक्रिया में बदल सकते हैं?

एसलैंड सर्वर में एरलैंड सोमरसकोग के लेख एरर हैंडलिंग से - एक पृष्ठभूमि :

उपयोगकर्ता द्वारा परिभाषित कार्यों को आमतौर पर SET, SELECT, INSERT, UPDATE या DELETE स्टेटमेंट के हिस्से के रूप में आमंत्रित किया जाता है। मैंने जो पाया है वह यह है कि यदि मल्टी-स्टेटमेंट टेबल-वैल्यू फ़ंक्शन में या स्केलर फ़ंक्शन में कोई त्रुटि दिखाई देती है, तो फ़ंक्शन का निष्पादन तुरंत समाप्त हो जाता है, और इसलिए यह कथन फ़ंक्शन का हिस्सा है। जब तक त्रुटि बैच समाप्त नहीं हो जाती, तब तक अगली पंक्ति पर निष्पादन जारी रहता है। या तो मामले में, @@ त्रुटि 0. है। इस प्रकार, यह पता लगाने का कोई तरीका नहीं है कि त्रुटि टी-एसक्यूएल से हुई है।

इनलाइन तालिका-फ़ंक्शन के साथ समस्या प्रकट नहीं होती है, क्योंकि एक इनलाइन तालिका-मूल्यवान फ़ंक्शन मूल रूप से एक मैक्रो है जो क्वेरी प्रोसेसर क्वेरी में चिपकाता है।

आप EXEC स्टेटमेंट के साथ स्केलर फ़ंक्शंस को भी निष्पादित कर सकते हैं। इस स्थिति में, निष्पादन तब होता है जब कोई त्रुटि उत्पन्न होती है (जब तक कि यह बैच-एबॉर्टिंग त्रुटि नहीं हो)। @@ त्रुटि सेट है, और आप फ़ंक्शन के भीतर @@ त्रुटि के मूल्य की जांच कर सकते हैं। हालाँकि, यह कॉल करने वाले को त्रुटि का संचार करने के लिए समस्याग्रस्त हो सकता है।


4

शीर्ष उत्तर आम तौर पर सबसे अच्छा होता है, लेकिन इनलाइन टेबल वैल्यू फ़ंक्शंस के लिए काम नहीं करता है।

माइकटेवी ने शीर्ष उत्तर पर अपनी टिप्पणी में इसके लिए एक समाधान दिया, लेकिन इसके लिए मैक्स जैसे कुल कार्य का उपयोग आवश्यक था, जो मेरी परिस्थिति के लिए अच्छा नहीं था।

मैंने उस मामले के लिए एक वैकल्पिक समाधान के साथ गड़बड़ कर दी, जहां आपको एक इनलाइन टेबल मूल्यवान udf की आवश्यकता होती है, जो एक समुच्चय के बजाय कुछ चुनिंदा * की तरह लौटाता है । इस विशेष मामले को हल करने वाला नमूना कोड नीचे है। जैसा कि किसी ने पहले ही बताया है ... "JEEZ wotta hack" :) मैं इस मामले के लिए किसी भी बेहतर समाधान का स्वागत करता हूं!

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
किसी को भी पढ़ने के लिए, मैं संभावित प्रदर्शन प्रभाव को देखने नहीं था ... मैं हैरान नहीं किया जाएगा अगर हैक संघ + मामले बयान नीचे धीमा कर देती बातें ...
davec

4

कुछ लोग टेबल-वेल्यूड फ़ंक्शंस में त्रुटियों को बढ़ाने के बारे में पूछ रहे थे, क्योंकि आप " RETURN [अमान्य कास्ट] " चीजों का उपयोग नहीं कर सकते हैं। एक चर के लिए अमान्य कलाकारों को भी काम करता है।

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

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

RETURN

END

इसका परिणाम यह होगा:

Msg 245, स्तर 16, राज्य 1, लाइन 14 रूपांतरण तब विफल हो गया जब varchar value 'booooom!' डेटा प्रकार int के लिए।


2

मैं टेबल वैल्यू फंक्शन के बारे में davec के उत्तर के तहत टिप्पणी नहीं कर सकता, लेकिन मेरी विनम्र राय में यह आसान समाधान है:

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

एक तरीका (हैक) एक फ़ंक्शन / संग्रहीत कार्यविधि है जो एक अमान्य कार्रवाई करता है। उदाहरण के लिए, निम्न छद्म एसक्यूएल

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, स्तंभ पर एक अद्वितीय बाधा है ir_msg। इसका एक साइड-इफ़ेक्ट (कम से कम MySQL पर) है, इरेटीएमएसजी के मूल्य का उपयोग उस अपवाद के विवरण के रूप में किया जाता है जब यह एप्लिकेशन स्तर के अपवाद ऑब्जेक्ट में वापस हो जाता है।

मुझे नहीं पता कि आप SQL सर्वर के साथ कुछ ऐसा कर सकते हैं, लेकिन एक शॉट के लायक है।


5
दिलचस्प विचार है, लेकिन INSERT को एक फ़ंक्शन में भी अनुमति नहीं है।
EMP
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.