SQL Server 사용자 정의 함수에서 오류를보고하는 방법


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을 반환 할 수는 있지만 기능을 사용하는 개발자는이 문제를 해결하기가 어려울 수 있습니다. 또한 0으로 나누거나 그와 비슷한 것을 유발할 수 있습니다. 이로 인해 오류 메시지가 생성되지만 오해의 소지가 있습니다. 어떻게 든 내 자신의 오류 메시지를보고 할 수있는 방법이 있습니까?

답변:


223

CAST를 사용하여 의미있는 오류를 발생시킬 수 있습니다.

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

그런 다음 Sql Server는 몇 가지 도움말 정보를 표시합니다.

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

112
좋은 대답이지만 JEEZ는 해킹을해야합니다. > :(
JohnL4

5
RETURN이 간단한 선택 인라인 테이블 값 함수의 경우 아무것도 반환되지 않기 때문에이 기능이 작동하지 않습니다. 널조차도 아니고 내 경우에는 아무것도 발견되지 않으면 오류를 발생시키고 싶었습니다. 명백한 성능상의 이유로 인라인 함수를 다중 통계 함수로 나누고 싶지 않았습니다. 대신 귀하의 솔루션과 ISNULL 및 MAX를 사용했습니다. RETURN 통계는 이제 다음과 같습니다. SELECT ISNULL (MAX (E.EntityID), CAST ( 'The Lookup ('+ @LookupVariable + ')이 없습니다.'는 Int)) [EntityID] FROM Entity as E WHERE E. 조회 = @ LookVariable
MikeTeeVee

예, 오류를 던질 수 있지만 조건부로 오류를 던질 수는 없습니다. 코드 경로에 관계없이 함수가 실행됩니다.
satnhak

10
훌륭한 솔루션이지만 TVF를 사용하는 사람들에게는 이것이 쉽게 수익의 일부가 될 수 없습니다. 사람들을 위해 :declare @error int; set @error = 'Error happened here.';
팀 레너

20
나는 천 개의 불타는 태양의 힘으로 이것을 싫어한다. 다른 옵션이 없습니까? 좋아. 그러나 cripes ...
Remi Despres-Smyth

18

일반적인 트릭은 0으로 나누기를 강제하는 것입니다. 이렇게하면 오류가 발생하고 함수를 평가하는 현재 명령문이 중단됩니다. 개발자 또는 지원 담당자가이 동작에 대해 알고 있으면 0으로 나누기 오류가 관련이없는 다른 문제의 증상으로 이해되므로 문제를 조사하고 해결하는 것이 매우 쉽습니다.

불행히도 현재 SQL 함수의 디자인은 더 나은 선택을 허용하지 않습니다. RAISERROR 사용은 기능에서 절대적으로 허용되어야합니다.


7

Vladimir Korolev의 답변에 따르면 조건부로 오류를 던지는 관용구는 다음과 같습니다.

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또는 @@ERRORUDF를 사용할 수 없습니다. UDF를 획기적인 절차로 바꿀 수 있습니까?

Erland Sommarskog의 기사 SQL Server의 오류 처리 – 배경 :

사용자 정의 함수는 일반적으로 SET, SELECT, INSERT, UPDATE 또는 DELETE 문의 일부로 호출됩니다. 내가 찾은 것은 다중 문 테이블 반환 함수 또는 스칼라 함수에 오류가 표시되면 함수 실행이 즉시 중단되고 함수의 일부 문장도 있다는 것입니다. 오류가 배치를 중단하지 않는 한 다음 줄에서 실행이 계속됩니다. 두 경우 모두 @@ error는 0입니다. 따라서 T-SQL의 함수에서 오류가 발생했음을 감지 할 방법이 없습니다.

인라인 테이블 반환 함수는 기본적으로 쿼리 프로세서가 쿼리에 붙여 넣는 매크로이므로 인라인 테이블 함수에는 문제가 나타나지 않습니다.

EXEC 문으로 스칼라 함수를 실행할 수도 있습니다. 이 경우, 오류가 발생하면 (일괄 중단 오류가 아닌 한) 실행이 계속됩니다. @@ error가 설정되고 함수 내에서 @@ error의 값을 확인할 수 있습니다. 그래도 오류를 발신자에게 알리는 것은 문제가 될 수 있습니다.


4

최상위 답변이 일반적으로 가장 좋지만 인라인 테이블 반환 함수에는 작동하지 않습니다.

MikeTeeVee는 최고 답변에 대한 그의 의견에 이것에 대한 해결책을 주었지만 MAX와 같은 집계 함수를 사용해야했지만 내 상황에 잘 맞지 않았습니다.

집계 대신 select * 와 같은 것을 반환하는 인라인 테이블 값 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
읽는 사람에게는 잠재적 인 성능 영향을 보지 못했습니다 ... 핵 조합 + 사례 진술로
인해

4

" RETURN [invalid cast] "를 사용할 수 없기 때문에 몇몇 사람들은 Table-Valued 함수에서 오류 발생에 대해 질문 했습니다. 유효하지 않은 캐스트를 변수에 할당하는 것도 마찬가지로 작동합니다.

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

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

RETURN

END

결과 :

메시지 245, 수준 16, 상태 1, 줄 14 varchar 값 '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

한 가지 방법 (해킹)은 잘못된 작업을 수행하는 함수 / 저장 프로 시저를 갖는 것입니다. 예를 들어, 다음 의사 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 테이블에서 err_msg 컬럼에 대한 고유 제한 조건이 있습니다. 이것의 부작용 (적어도 MySQL에서는)은 err_msg의 값이 응용 프로그램 수준 예외 개체로 돌아올 때 예외에 대한 설명으로 사용된다는 것입니다.

SQL Server와 비슷한 작업을 수행 할 수 있는지 모르지만 한 번 가치가 있습니다.


5
흥미로운 아이디어이지만 INSERT도 함수에서 허용되지 않습니다.
EMP
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.