用户定义函数中的Try-Catch?

时间:2010-07-08 17:12:14

标签: tsql user-defined-functions

我正在尝试编写一个UDF来将一个guid或与该guid相关联的项目代码的字符串转换为guid:

CREATE FUNCTION fn_user_GetProjectID 
(
    @Project nvarchar(50)
)
RETURNS uniqueidentifier
AS
BEGIN

    declare @ProjectID uniqueidentifier

    BEGIN TRY
        set @ProjectID = cast(@Project as uniqueidentifier)
    END TRY
    BEGIN CATCH
        set @ProjectID = null
    END CATCH

    if(@ProjectID is null)
    BEGIN
        select  @ProjectID = ProjectID from Project where projectcode = @Project
    END

    return @ProjectID

END

如果上面的代码嵌入在我的存储过程中,这样可以正常工作,但是我想用它来创建一个函数,以便我遵循DRY。

当我尝试创建函数时,我得到这样的错误:

Msg 443, Level 16, State 14, Procedure fn_user_GetProjectID, Line 16
Invalid use of side-effecting or time-dependent operator in 'BEGIN TRY' within a function.

有没有人知道如何解决这个错误?

编辑:我知道我不能在函数中使用Try-Catch,我想简化的问题是,有没有办法进行转换,如果转换失败,只返回NULL,而不是错误?

6 个答案:

答案 0 :(得分:3)

显然你不能在UDF中使用TRY-CATCH。

根据bug-reporting page for SQL Server

  

联机丛书记录了这种行为,   在主题“创建功能   (Transact-SQL)“:”以下内容   语句在函数中有效:   [...] Control-of-Flow语句   除了TRY ... CATCH语句。 [...]“

但是他们在2006年为未来带来了希望:

  

然而,这是一个严重的限制   应该在将来删除   发布。你应该发表一个建议   在这方面,我会   全心全意地投票支持它。

答案 1 :(得分:2)

来自MSDN

  

的列或局部变量   uniqueidentifier数据类型可以   初始化为。中的值   以下方式:

     

使用NEWID功能。

     

通过转换为字符串常量   在形式   XXXXXXXXXXXX-XXXXXXXX-XXXXXXXXXXXX,   其中每个x是十六进制数字   在0-9或a-f范围内。

     

例如,   6F9619FF-8B86-D011-B42D-00C04FC964FF   是一个有效的uniqueidentifier值。

您可以使用模式匹配来验证字符串。请注意,这不适用于减少GUID大小的特定编码:

declare @Project nvarchar(50) 

declare @ProjectID uniqueidentifier 
declare @HexPattern nvarchar(268) 

set @HexPattern =  
    '[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +  
    '[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +  
    '[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +  
    '[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' 

/* Take into account GUID can have curly-brackets or be missing dashes */
/* Note: this will not work for GUIDs that have been specially encoded */
set @Project = '{' + CAST(NEWID() AS VARCHAR(36)) + '}'

select @Project

set @Project = REPLACE(REPLACE(REPLACE(@Project,'{',''),'}',''),'-','')

/* Cast as uniqueid if pattern matches, otherwise return null */ 
if @Project LIKE @HexPattern 
  select @ProjectID = CAST(
         SUBSTRING(@Project,1,8) + '-' + 
         SUBSTRING(@Project,9,4) + '-' + 
         SUBSTRING(@Project,13,4) + '-' + 
         SUBSTRING(@Project,17,4) + '-' + 
         SUBSTRING(@Project,21,LEN(@Project)-20)
         AS uniqueidentifier) 

select @ProjectID

答案 2 :(得分:1)

  

我知道我不能在函数中使用Try-Catch,我想一个简化的问题是,有没有一种方法可以进行强制转换,如果强制转换失败,它将只返回NULL,错误

从SQL Server 2012开始,您可以使用TRY_CAST / TRY_CONVERT函数:

  

如果转换成功,则将值转换为指定的数据类型;否则,返回null。

CREATE FUNCTION fn_user_GetProjectID(@Project nvarchar(50))
RETURNS uniqueidentifier
AS
BEGIN
  declare @ProjectID uniqueidentifier = TRY_CAST(@Project as uniqueidentifier);

  IF(@ProjectID is null)
  BEGIN
     select @ProjectID = ProjectID from Project where projectcode = @Project;
  END

  return @ProjectID;
END

答案 3 :(得分:0)

不确定,但为什么不把它翻转......乍一看我会简化它:

select @ProjectID = 
   ISNULL((select ProjectID from Project where 
           projectcode = @Project)
     ,(cast @Project as uniqueidentifier))

如果这没有提供足够的错误处理,我确信有更好的方法来预先检查演员是否可以在不使用try / catch的情况下工作......

答案 4 :(得分:0)

我的强力方法是创建我自己的ToGuid()函数,验证它是否可以先转换为GUID,否则返回null。它可能不是很快,但它完成了工作,如果它是一个而不是试图在表中查找它可能更快转换guid。编辑:我的意思是赞美这个博客,我得到了这个函数的代码基础:http://jesschadwick.blogspot.com/2007/11/safe-handling-of-uniqueidentifier-in.html

CREATE FUNCTION [dbo].[ToGuid]
(
    @input NVARCHAR(MAX)
)
RETURNS uniqueidentifier
AS
BEGIN 
    DECLARE @isValidGuid BIT; 
    DECLARE @temp NVARCHAR(MAX); 
    SET @isValidGuid = 1; 
    SET @temp = UPPER(LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(@input, '-', ''), '{', ''), '}', '')))); 
    IF(@temp IS NOT NULL AND LEN(@temp) = 32) 
    BEGIN  
        DECLARE @index INT;  
        SET @index = 1
        WHILE (@index <= 32)  
        BEGIN   
            IF (SUBSTRING(@temp, @index, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F'))    
            BEGIN
                SET @index = @index + 1
            END
            ELSE    
            BEGIN
                SET @isValidGuid = 0
                BREAK;     
            END
        END    
    END
    ELSE
    BEGIN
        SET @isValidGuid = 0
    END  

    DECLARE @ret UNIQUEIDENTIFIER
    IF(@isValidGuid = 1) 
        set @ret = cast(@input AS UNIQUEIDENTIFIER)
    ELSE
        set @ret = NULL

    RETURN @ret

END

如果有更好的答案,我仍然非常感兴趣。

答案 5 :(得分:0)

使用ISNUMERIC函数验证@Project是否为数字。

您的代码应如下所示:

declare @ProjectID uniqueidentifier

set @ProjectID = null

IF ISNUMERIC(@Project) > 0
BEGIN
    set @ProjectID = cast(@Project as uniqueidentifier)
END

if(@ProjectID is null)
BEGIN
    select  @ProjectID = ProjectID from Project where projectcode = @Project
END

return @ProjectID