如何报告从SQL Server用户自定义函数的错误(How to report an error f

2019-07-19 20:01发布

我在写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,但它会使用功能来解决这个任何开发人员很困难。 我还可以通过零或类似的东西引起一个部门 - 这会产生错误信息,但一个误导性的。 有什么办法,我可以有我自己的错误消息不知何故报道?

Answer 1:

您可以使用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.


Answer 2:

惯用的伎俩是给力除以0。这将发出一个错误并中断正在评估函数当前语句。 如果开发人员或支持人员知道这种行为,调查和故障排除的问题是很容易由0错误的划分被理解为一个不同的,不相关的问题的症状。

那么糟糕,因为这看起来从任何角度来看,此刻的SQL函数不幸的是,设计允许没有更好的选择。 使用RAISERROR要绝对函数被允许。



Answer 3:

从弗拉基米尔·科罗廖夫的答案继,成语有条件地抛出一个错误

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)    


Answer 4:

我认为,最彻底的方法是只接受,如果无效的参数传递函数可以返回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


Answer 5:

RAISEERROR@@ERROR在UDF是不允许的。 你可以把UDF成strored程序?

从厄兰Sommarskog的文章在SQL Server错误处理-一个背景 :

用户定义的函数通常被调用作为一组,SELECT,INSERT,UPDATE或DELETE语句的一部分。 我发现的是,如果出现在多语句表值函数或标量函数的错误,则函数的执行被立即中止,所以是功能的一部分的说法。 继续执行下一行,除非错误中止批处理。 在任一情况下,错误@@是0。因此,就没有方法来检测,在从T-SQL函数发生了错误。

该问题不会与内嵌表函数出现,由于内嵌表值函数基本上是一个宏查询处理器糊剂到查询。

您也可以用EXEC语句执行标量函数。 在这种情况下,继续执行如果发生错误,(除非它是分批中止错误)。 @@ ERROR设置,你可以在函数中检查@@ ERROR的值。 它可以是有问题的,虽然错误传达给调用者。



Answer 6:

顶端回答通常是最好的,但对于内联表值函数不起作用。

MikeTeeVee了在他的顶端回答评论了一个解决方案,但它需要使用像MAX聚合函数,它并没有对我的情况很好地工作的。

我搞砸与周围当您需要一个内联表值UDF返回类似选择*而不是聚集的情况下,替代的解决方案。 示例代码解决这种特殊情况如下。 正如有人已经指出了......“哎呀wotta黑客” :)我欢迎这种情况下,任何更好的解决方案!

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


Answer 7:

有几个人在问表值函数提高的错误,因为你不能使用“RETURN [无效转换]”之类的东西。 分配无效投给一个变量的作品一样好。

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数据类型。



Answer 8:

下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


Answer 9:

单程(一个黑客)是具有执行一个无效的动作的功能/存储过程。 例如,下面的伪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类似的东西,但值得一试。



文章来源: How to report an error from a SQL Server user-defined function