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

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으로 나누거나 그와 비슷한 것을 유발할 수 있습니다. 이로 인해 오류 메시지가 생성되지만 오해의 소지가 있습니다. 어떻게 든 내 자신의 오류 메시지를보고 할 수있는 방법이 있습니까?



답변

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.


답변

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

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


답변

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)    


답변

가장 깨끗한 방법은 잘못된 인수가 전달되면 함수가 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


답변

RAISEERROR또는 @@ERRORUDF를 사용할 수 없습니다. UDF를 획기적인 절차로 바꿀 수 있습니까?

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

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

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

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


답변

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

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


답변

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.