【问题标题】:Try-Catch in User Defined Function?用户定义函数中的 Try-Catch?
【发布时间】:2011-03-13 11:35:35
【问题描述】:

我正在尝试编写一个 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,而不是错误?

【问题讨论】:

  • 使用 SQLCLR 是一种选择吗?如果是这样,编写自己的 SQLCLR 函数就可以了。
  • SQLCLR 绝对不是一个选项。会容易得多,是的。我最终使用了我的解决方案和 8kb 的组合。

标签: tsql user-defined-functions


【解决方案1】:

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

据此bug-reporting page for SQL Server

Books Online 记录了这种行为, 在主题“创建功能 (Transact-SQL)": "以下 语句在函数中有效: [...] 流量控制语句 除了 TRY...CATCH 语句。 [...]”

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

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

【讨论】:

  • 嗯,是的,我知道我不能使用 Try-Catch,这就是我要求替代品的原因。 Fosco 的方法也可以,但如果转换失败,我不希望出现错误。
【解决方案2】:

来自MSDN

一列或局部变量 uniqueidentifier 数据类型可以是 初始化为 以下方式:

通过使用 NEWID 函数。

通过从字符串常量转换 在表格中 xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, 其中每个 x 是一个十六进制数字 在 0-9 或 a-f 范围内。

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

您可以使用模式匹配来验证字符串。请注意,这不适用于减小 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

【讨论】:

  • 这实际上并没有考虑到 uniqueidentifiers 也可以用 {} 包围,而且它们甚至可能没有破折号。它比我的版本更紧凑,但在某些情况下会失败。我会从你的@Hexpattern 中取出破折号,然后在我进行测试之前,去掉破折号和花括号。
  • 对...我没有在 SQL Server 之外思考。
  • 您实际上也不必在进行演员表之前将破折号 放回,但是您对模式有一种新颖的方法..它比我的循环。我会给你这个。
【解决方案3】:

我知道我不能在函数中使用 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

【讨论】:

    【解决方案4】:

    不确定,但为什么不把它翻过来……乍一看,我会这样简化:

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

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

    【讨论】:

    • 如果@Project 不是真正的代码,但也不是唯一标识符,它并不能解决强制转换错误的问题。而且,它把慢车放在快马之前。首先检查它是否是唯一标识符比尝试在表中查找要快得多。
    • 就像我说的,我确信有更好的方法来验证演员阵容是否有效。它必须全是数字,对吗?另外,查询真的那么慢吗?...
    • 是的,我有几个地方正在这样做,一个是几百万行。一个项目可能只有几百个。
    【解决方案5】:

    我的蛮力方法是创建我自己的 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
    

    如果有比这更好的答案,我仍然很感兴趣。

    【讨论】:

      【解决方案6】:

      使用 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-21
        • 2013-07-26
        • 1970-01-01
        • 2011-07-06
        • 2021-12-19
        • 1970-01-01
        • 1970-01-01
        • 2020-01-21
        相关资源
        最近更新 更多