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)
Rem*_*anu 15
通常的技巧是强制除以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)
Run Code Online (Sandbox Code Playgroud)
我认为最简单的方法是接受如果传递无效参数,函数可以返回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)
RAISEERROR
或者@@ERROR
不允许在UDF中使用.你能把UDF变成一个有条理的程序吗?
来自Erland Sommarskog的文章SQL Server中的错误处理 - 背景:
用户定义的函数通常作为SET,SELECT,INSERT,UPDATE或DELETE语句的一部分调用.我发现如果在多语句表值函数或标量函数中出现错误,函数的执行会立即中止,函数所属的语句也是如此.除非错误中止批处理,否则将在下一行继续执行.在任何一种情况下,@@ error都是0.因此,无法从T-SQL检测到函数中发生错误.
内联表函数不会出现此问题,因为内联表值函数基本上是查询处理器粘贴到查询中的宏.
您还可以使用EXEC语句执行标量函数.在这种情况下,如果发生错误,则继续执行(除非是批量中止错误).设置了@@ error,您可以在函数中检查@@ error的值.但是,将错误传达给调用者可能会有问题.
最佳答案通常是最好的,但不适用于内联表值函数。
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)
一些人询问在表值函数中引发错误,因为您不能使用“ 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。