Try-Catch в пользовательской функции? - PullRequest
4 голосов
/ 08 июля 2010

Я пытаюсь написать 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, если приведение не выполнено, вместо ошибки?

Ответы [ 5 ]

3 голосов
/ 08 июля 2010

Очевидно, вы не можете использовать TRY-CATCH в UDF.

Согласно этой странице отчетов об ошибках для SQL Server :

Books Online документирует это поведение, в теме "СОЗДАТЬ ФУНКЦИЮ (Transact-SQL) ":" следующее операторы действительны в функции: [...] операторы Control-of-Flow кроме TRY ... CATCH заявлений. [...] "

Но они давали надежду на будущее еще в 2006 году:

Однако это серьезное ограничение это должно быть удалено в будущем релиз. Вы должны опубликовать предложение в связи с этим и я буду искренне голосуйте за него.

2 голосов
/ 09 июля 2010

С MSDN :

Столбец или локальная переменная Тип данных uniqueidentifier может быть инициализируется значением в следующие способы:

С помощью функции NEWID.

Преобразованием из строковой константы в виде XXXXXXXXXXXX-XXXXXXXX-XXXXXXXXXXXX, в котором каждый х является шестнадцатеричной цифрой в диапазоне 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
0 голосов
/ 05 сентября 2013

Проверьте, является ли @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
0 голосов
/ 08 июля 2010

Мой метод грубой силы заключался в создании моей собственной функции ToGuid (), которая проверяет, может ли она быть сначала преобразована в GUID, если нет, она возвращает ноль. Он может быть не очень быстрым, но он выполняет свою работу, и, вероятно, быстрее конвертировать 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 голосов
/ 08 июля 2010

Не уверен, но почему бы не перевернуть его ... на первый взгляд, я бы упростил это так:

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

Если это не обеспечивает достаточной обработки ошибок, я уверен, что есть лучший способ предварительно проверить, что приведение может работать без использования try / catch ...

...