2010-07-08 20 views
4

我試圖寫一個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 ?

+0

是使用SQLCLR的選擇嗎?如果是這樣,編寫您自己的SQLCLR函數將工作。 – 2010-07-09 05:05:46

+0

SQLCLR絕對不是一種選擇。會更容易,是的。我最終使用了我的解決方案和8kb的組合。 – Moose 2010-07-09 13:25:29

回答

2

MSDN

A柱或 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 
+0

這實際上並未考慮到uniqueidentifiers也可以用{}包圍,而且它們甚至可能沒有破折號。它比我的版本更緊湊,但在某些情況下會失敗。我會從你的@Hexpattern中取出破折號,然後在做測試之前,去掉破折號和花括號。 – Moose 2010-07-08 23:56:11

+0

對......我沒有想到SQL Server之外。 – 8kb 2010-07-09 00:14:54

+0

修改代碼以處理大括號和缺少的破折號... – 8kb 2010-07-09 00:43:20

0

不知道,但爲什麼不甩掉它周圍......乍一看我將簡化這樣的:

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

如果不能提供足夠的錯誤處理,我敢肯定有一個更好的預先檢查演員陣容可以在沒有使用try/catch的情況下工作...

+0

如果@Project不是一個真正的代碼,但它不能解決拋出錯誤的問題,但它不是一個唯一標識符。並且,它將慢車推到快馬之前。更快地檢查它是否是唯一標識符,而不是嘗試去查看它在表格中。 – Moose 2010-07-08 17:45:02

+0

就像我說過的,我確定有更好的方法來驗證演員陣容的功能。它必須是全部數字,對嗎?另外,這個查詢真的很慢嗎?... – Fosco 2010-07-08 17:55:29

+0

是的,我有幾個地方我在做這個,一個是有幾百萬行。該項目可能只有幾百個。 – Moose 2010-07-09 01:04:40

3

顯然,您不能在UDF中使用TRY-CATCH。

根據這一bug-reporting page for SQL Server

聯機叢書文檔此行爲, 參閱 「CREATE FUNCTION (的Transact-SQL)」:「下面的語句 的功能是有效的: [.. ]控制,流語句 除了的try ... catch語句[...]」

但他們在2006年被放棄對未來的希望回來。

但是,這是一個嚴重的限制 應在將來的 版本中刪除。您應該在這方面發佈建議 ,我會全心全意投票給它。

+0

嗯,是的,我知道我不能使用Try-Catch,這是我要求的替代品。福斯科的方法也會起作用,但如果演員失敗,我不想要這個錯誤。 – Moose 2010-07-08 17:41:33

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 

我還在,如果有比這更好的答案很感興趣。

0

驗證是否@project是使用ISNUMERIC函數的一個數字。

你的代碼應該看起來像這樣:

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