如何从SQL Server用户定义的函数报告错误

EMP*_*EMP 143 sql t-sql sql-server user-defined-functions sql-server-2008

我正在SQL Server 2008中编写一个用户定义的函数.我知道函数不能以通常的方式引发错误 - 如果您尝试包含RAISERROR语句SQL返回:

Msg 443, Level 16, State 14, Procedure ..., Line ...
Invalid use of a side-effecting operator 'RAISERROR' within a function.
Run Code Online (Sandbox Code Playgroud)

但事实是,该函数需要一些输入,这可能是无效的,如果是,则函数可以返回没有有意义的值.那我该怎么办?

当然,我可以返回NULL,但是使用该函数的任何开发人员都很难对此进行故障排除.我也可能导致除零或类似的东西 - 这会产生错误信息,但会产生误导性信息.有什么方法可以以某种方式报告我自己的错误消息吗?

Vla*_*lev 214

您可以使用CAST抛出有意义的错误:

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

然后Sql Server将显示一些帮助信息:

Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'Error happened here.' to data type int.
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,但JEEZ wotta hack.> :( (103认同)
  • 我讨厌这个有一千个燃烧太阳的力量.没有其他选择?精细.但是皱纹...... (14认同)
  • 很好的解决方案,但对于那些使用TVF的人来说,这不能轻易成为回报的一部分.对于那些:`declare @error int; 设置@error ='错误发生在这里.'; ` (9认同)
  • 对于一个内联表值函数,其中RETURN是一个简单的选择,单独这不起作用,因为没有返回 - 甚至不是null,在我的情况下,我想在没有找到任何内容时抛出错误.出于显而易见的性能原因,我不想将内联函数分解为多个参数.相反,我使用你的解决方案加上ISNULL和MAX.RETURN语句现在如下所示:SELECT ISNULL(MAX(E.EntityID),CAST('Lookup('+ @LookupVariable +')不存在.'作为Int))[EntityID] FROM Entity as E WHERE E. Lookup = @ LookupVariable (4认同)

Rem*_*anu 15

通常的技巧是强制除以0.这将引发错误并中断正在评估函数的当前语句.如果开发人员或支持人员知道此行为,则调查问题并对其进行故障排除相当容易,因为除以0错误被理解为不同的,无关的问题的症状.

从任何角度来看都很糟糕,遗憾的是,SQL函数的设计目前还没有更好的选择.在函数中绝对允许使用RAISERROR.


bri*_*ler 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)    
Run Code Online (Sandbox Code Playgroud)


And*_*dyM 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
Run Code Online (Sandbox Code Playgroud)


Mit*_*eat 5

RAISEERROR或者@@ERROR不允许在UDF中使用.你能把UDF变成一个有条理的程序吗?

来自Erland Sommarskog的文章SQL Server中的错误处理 - 背景:

用户定义的函数通常作为SET,SELECT,INSERT,UPDATE或DELETE语句的一部分调用.我发现如果在多语句表值函数或标量函数中出现错误,函数的执行会立即中止,函数所属的语句也是如此.除非错误中止批处理,否则将在下一行继续执行.在任何一种情况下,@@ error都是0.因此,无法从T-SQL检测到函数中发生错误.

内联表函数不会出现此问题,因为内联表值函数基本上是查询处理器粘贴到查询中的宏.

您还可以使用EXEC语句执行标量函数.在这种情况下,如果发生错误,则继续执行(除非是批量中止错误).设置了@@ error,您可以在函数中检查@@ error的值.但是,将错误传达给调用者可能会有问题.


dav*_*vec 5

最佳答案通常是最好的,但不适用于内联表值函数。

MikeTeeVee 在他对最佳答案的评论中为此提供了一个解决方案,但它需要使用像 MAX 这样的聚合函数,这在我的情况下效果不佳。

在需要一个内联表值 udf 的情况下,我使用了一个替代解决方案,该 udf 返回类似select *而不是聚合的内容。解决这种特殊情况的示例代码如下。正如有人已经指出的那样...... “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
Run Code Online (Sandbox Code Playgroud)


Nig*_*vel 5

一些人询问在表值函数中引发错误,因为您不能使用“ RETURN [invalid cast] ”之类的东西。将无效的强制转换分配给变量也同样有效。

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

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

RETURN

END
Run Code Online (Sandbox Code Playgroud)

这导致:

消息 245,级别 16,状态 1,第 14 行转换 varchar 值 'booooom!' 时转换失败 到数据类型int。