Ошибка SQLCLR .NET: ссылка на объект не указывает на экземпляр объекта

В настоящее время я получаю сообщение об ошибке при попытке выполнить простую инструкцию SELECT, которая ссылается на сборку, содержащую код C# для алгоритма расстояния Джаро-Винклера. Я впервые работаю с SQLCLR.

Я могу успешно запустить код ниже без дополнительного оператора OR

Пример:

SELECT
    * 
FROM
    USERS
WHERE
(
    DATE_OF_BIRTH IS NOT NULL 
    AND DBO.JAROWINKLER(CONVERT(VARCHAR(6),DATE_OF_BIRTH,12),@DOB) > 0.9
)
OR
(
    USERID = @USERID
)

Однако, когда я включаю оператор OR, я получаю это сообщение об ошибке:

Произошла ошибка .NET Framework во время выполнения определяемой пользователем подпрограммы или агрегата "JaroWinkler":

System.NullReferenceException: ссылка на объект не указывает на экземпляр объекта. System.NullReferenceException: at JaroWinklerDistanceCLR.JaroWinklerDistance.proximity(String aString1, String aString2)

Код ниже работает и делает то, что нужно. Мне просто интересно, есть ли другой способ? В идеале я бы хотел, чтобы он содержал один оператор SELECT. Я понятия не имею, к чему относится приведенная выше ошибка, в столбце UserID нет значений NULL.

Набор разрешений для сборки установлен на Безопасный.

Рабочий пример:

SELECT
    *
FROM
    USERS
WHERE
    DATE_OF_BIRTH IS NOT NULL 
    AND DBO.JAROWINKLER(CONVERT(VARCHAR(6),DATE_OF_BIRTH,12),@DOB) > 0.9

UNION ALL

SELECT
    *
FROM
    USERS
WHERE
    USERID = @USERID

person Zakerias    schedule 27.07.2017    source источник
comment
Итак, вы автор JAROWINKLER и спрашиваете, как исправить исключение нулевой ссылки?   -  person Alex K.    schedule 27.07.2017
comment
@АлексК. Нет, я передаю значения алгоритму расстояния. Моя проблема связана с оператором OR в SQL. en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance   -  person Zakerias    schedule 27.07.2017


Ответы (3)


Два других ответа - обходные пути, а не решения. И этот обходной путь придется дублировать везде, где используется эта функция, а это очень подвержено ошибкам и сложно в обслуживании.

Прежде чем перейти к основной проблеме, рассмотрим небольшую связанную с ней проблему, которую следует решить в первую очередь: неправильные типы входных параметров. Для методов SQLCLR следует использовать типы Sql* вместо стандартных типов .NET. Для этого конкретного кода это означает использование SqlString вместо String. Подробнее о том, почему SqlString вместо String, см. в моем ответе на следующий S.O. вопрос: Должен ли я используйте SqlString или строку в качестве типа параметра для пользовательской функции SQLCLR.

Теперь проблема в том, что код SQLCLR неправильно обрабатывает NULLs. К счастью, заставить его обращаться с ними несложно. Есть два варианта, в зависимости от того, может ли любой из входных параметров принимать NULL или нет:

  • Если какой-либо из входных параметров может допустимо передать NULL, вам необходимо обработать это в коде (и это также относится ко всем случаям при работе с табличными функциями и хранимыми процедурами). И вы регистрируете код через свойство .IsNull, которое есть у всех типов Sql*. Например (при условии, что aString2 может пройти в NULL):

    if (aString1.IsNull)
    {
      return SqlDouble.Null;
    }
    
  • Если ни один из входных параметров не может допустимо принять NULL, то вам следует обойти всю обработку без ввода кода, создав скалярную пользовательскую функцию с параметром WITH RETURNS NULL ON NULL INPUT оператора CREATE FUNCTION. При установке этого параметра, если какой-либо входной параметр имеет значение NULL, код пропускается и предполагается возвращаемое значение NULL. Обратите внимание, что это работает только со скалярными пользовательскими функциями и методами пользовательского типа.

Дополнительные сведения о работе с SQLCLR в целом см. в серии статей, которые я пишу по этой теме на SQL Server Central (для чтения контента на этом сайте требуется бесплатная регистрация): Лестница к SQLCLR.


В связи с этим я бы поставил под сомнение использование функции расстояния между строками для выполнения довольно простого вычисления даты. Я думаю, что этот код значительно выиграл бы от замены функции JaroWinkler на DATEDIFF на основе преобразования строки @DOB в DATE или DATETIME.

person Solomon Rutzky    schedule 27.07.2017
comment
Спасибо, Соломон, очень полезно. Буду реализовывать ваши предложения. - person Zakerias; 28.07.2017
comment
@Zakerias Не за что :-). И я только что добавил ссылку на свой ответ на вопрос (также на SO), в частности о SqlString vs String. - person Solomon Rutzky; 28.07.2017

Оператор OR, скорее всего, включает значения NULL в ваш набор результатов, которые возвращаются как другой тип данных. Попробуйте использовать

    OR (USERID = @USERID AND AND P.DATE_OF_BIRTH IS NOT NULL)

или, если вам требуются значения NULL, выберите имена полей явно (вместо выбора *) и оберните поле date_of_birth в операторе convert

person Richard Taylor    schedule 27.07.2017

Спасибо. NULL значений в поле даты рождения.

Работающий!

SELECT
    * 
FROM
    USERS
WHERE
(
    DBO.JAROWINKLER(CONVERT(VARCHAR(6),ISNULL(P.DATE_OF_BIRTH,''),12),@DOB) > 0.9
)
OR
(
    USERID = @USERID
)
person Zakerias    schedule 27.07.2017