Выгода попытки в определяемой пользователем функции?

Я пытаюсь записать UDF для перевода строки, которая является или гуидом или кодом проекта, связанным с тем гуидом в гуид:

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.

У кого-либо есть идея, как я могу обойти эту ошибку?

Править: Я знаю, что не могу использовать Выгоду Попытки в Функции, я предполагаю, упрощенные вопросы были бы, там способ сделать бросок, который просто возвратит ПУСТОЙ УКАЗАТЕЛЬ, если состав исполнителей перестанет работать вместо ошибки?

5
задан Moose 8 July 2010 в 17:43
поделиться

4 ответа

Из MSDN :

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

Используя функцию NEWID.

Путем преобразования из строковой константы в виде xxxxxxxx-xxxx-xxxx-xxxx-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
ответ дан 14 December 2019 в 13:25
поделиться

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

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

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

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

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

3
ответ дан 14 December 2019 в 13:25
поделиться

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

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

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

0
ответ дан 14 December 2019 в 13:25
поделиться

Мой метод перебора заключался в создании собственной функции ToGuid(), которая сначала проверяет, может ли она быть преобразована в GUID, если нет, то возвращает null. Это может быть не очень быстро, но это делает свою работу, и, вероятно, быстрее преобразовать guid, если он один, чем пытаться найти его в таблице. EDIT: Я хотел отдать должное этому блогу, где я взял основу моего кода для этой функции: 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
ответ дан 14 December 2019 в 13:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: