Как сравнить значения, которые могут быть как null - это T-SQL



Я хочу убедиться, что я не вставляю повторяющуюся строку в свою таблицу (например, только первичный ключ отличается). Все мое поле допускает нули, поскольку я решил, что null означает "все значения". Из-за нулей не может работать следующая инструкция в моей хранимой процедуре:



IF EXISTS(SELECT * FROM MY_TABLE WHERE 
MY_FIELD1 = @IN_MY_FIELD1 AND
MY_FIELD2 = @IN_MY_FIELD2 AND
MY_FIELD3 = @IN_MY_FIELD3 AND
MY_FIELD4 = @IN_MY_FIELD4 AND
MY_FIELD5 = @IN_MY_FIELD5 AND
MY_FIELD6 = @IN_MY_FIELD6)
BEGIN
goto on_duplicate
END


так как NULL = NULL не является истинным.



Как я могу проверить наличие дубликатов без оператора IF IS NULL для каждого столбца?

778   13  

13 ответов:

использовать INTERSECT оператора.

это NULL-чувствительный и эффективный, если у вас есть составной индекс для всех ваших полей:

IF      EXISTS
        (
        SELECT  MY_FIELD1, MY_FIELD2, MY_FIELD3, MY_FIELD4, MY_FIELD5, MY_FIELD6
        FROM    MY_TABLE
        INTERSECT
        SELECT  @IN_MY_FIELD1, @IN_MY_FIELD2, @IN_MY_FIELD3, @IN_MY_FIELD4, @IN_MY_FIELD5, @IN_MY_FIELD6
        )
BEGIN
        goto on_duplicate
END

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

аналогично @Эрика, но без использования 'NULL' символ.

(Field1 = Field2) OR (ISNULL(Field1, Field2) IS NULL)

это будет верно только в том случае, если оба значения non-NULL, и равны друг другу, или оба значения NULL

использовать ISNULL:

ISNULL(MY_FIELD1, 'NULL') = ISNULL(@IN_MY_FIELD1, 'NULL')

вы можете изменить 'NULL' что-то вроде 'All Values' если это имеет больше смысла, чтобы сделать это.

следует отметить, что с двумя аргументами, ISNULL работает так же, как COALESCE, который вы можете использовать, если у вас есть несколько значений для тестирования (т. е.-COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL')). COALESCE также возвращает после первого ненулевого значения, что означает, что это (незначительно) быстрее, если вы ожидаете, что MY_FIELD1 будет пустым. Однако, я нахожу ISNULL гораздо больше читаемый, так вот почему я использовал его здесь.

мне нужно было подобное сравнение при выполнении слияния:

WHEN MATCHED AND (Target.Field1 <> Source.Field1 OR ...)

дополнительные проверки должны избегать обновления строк, где все столбцы уже одинаковы. Для моих целей я хотел NULL <> anyValue чтобы быть правдой, и NULL <> NULL быть ложным.

решение развивалось следующим образом:

первая попытка:

WHEN MATCHED AND
(
    (
        -- Neither is null, values are not equal
        Target.Field1 IS NOT NULL
            AND Source.Field1 IS NOT NULL
            AND Target.Field1 <> Source.Field1
    )
    OR
    (
        -- Target is null but source is not
        Target.Field1 IS NULL
            AND Source.Field1 IS NOT NULL
    )
    OR
    (
        -- Source is null but target is not
        Target.Field1 IS NOT NULL
            AND Source.Field1 IS NULL
    )

    -- OR ... Repeat for other columns
)

вторая попытка:

WHEN MATCHED AND
(
    -- Neither is null, values are not equal
    NOT (Target.Field1 IS NULL OR Source.Field1 IS NULL)
        AND Target.Field1 <> Source.Field1

    -- Source xor target is null
    OR (Target.Field1 IS NULL OR Source.Field1 IS NULL)
        AND NOT (Target.Field1 IS NULL AND Source.Field1 IS NULL)

    -- OR ... Repeat for other columns
)

третья попытка (вдохновленный @тогда ответ):

WHEN MATCHED AND
(

    ISNULL(
        NULLIF(Target.Field1, Source.Field1),
        NULLIF(Source.Field1, Target.Field1)
    ) IS NOT NULL

    -- OR ... Repeat for other columns
)

в та же логика ISNULL/NULLIF может быть использована для проверки равенства и неравенства:

  • равенство: ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NULL
  • Inequaltiy: ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NOT NULL

вот SQL-Скрипка, демонстрирующая, как это работает http://sqlfiddle.com/#! 3 / 471d60/1

IF EXISTS(SELECT * FROM MY_TABLE WHERE 
            (MY_FIELD1 = @IN_MY_FIELD1 
                     or (MY_FIELD1 IS NULL and @IN_MY_FIELD1 is NULL))  AND
            (MY_FIELD2 = @IN_MY_FIELD2 
                     or (MY_FIELD2 IS NULL and @IN_MY_FIELD2 is NULL))  AND
            (MY_FIELD3 = @IN_MY_FIELD3 
                     or (MY_FIELD3 IS NULL and @IN_MY_FIELD3 is NULL))  AND
            (MY_FIELD4 = @IN_MY_FIELD4 
                     or (MY_FIELD4 IS NULL and @IN_MY_FIELD4 is NULL))  AND
            (MY_FIELD5 = @IN_MY_FIELD5 
                     or (MY_FIELD5 IS NULL and @IN_MY_FIELD5 is NULL))  AND
            (MY_FIELD6 = @IN_MY_FIELD6
                     or (MY_FIELD6 IS NULL and @IN_MY_FIELD6 is NULL)))
            BEGIN
                    goto on_duplicate
            END

многословный по сравнению с решением IFNULL / COALESCE. Но будет работать без необходимости думать о том, какое значение не будет отображаться в данных, которые могут быть использованы в качестве подставки для NULL.

вы могли бы объединить каждое значение, но это немного вызывает морщины:

    IF EXISTS(SELECT * FROM MY_TABLE WHERE 
    coalesce(MY_FIELD1,'MF1') = coalesce(@IN_MY_FIELD1,'MF1')  AND
    ...
    BEGIN
            goto on_duplicate
    END

вы также должны убедиться, что coalesced значение не является в противном случае допустимым значением для рассматриваемого столбца. Например, если бы было возможно, что значение MY_FIELD1 может быть 'MF1', то это вызвало бы много ложных попаданий.

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

Что делать, если вы хотите сделать сравнение для значений, которые не равны? Просто использование "не" перед ранее упомянутыми сравнениями не работает. Лучшее, что я мог придумать:

(Field1 <> Field2) OR (NULLIF(Field1, Field2) IS NOT NULL) OR (NULLIF(Field2, Field1) IS NOT NULL)

вы могли бы использовать SET ANSI_NULLS чтобы указать поведение операторов сравнения Equals ( = ) и Not Equal to ( ), когда они используются с нулевыми значениями.

вам придется использовать IS NULL или ISNULL. Там действительно нет away вокруг него.

NULLIF (TARGET.relation_id, источник.app_relation_id) является нулевым простым решением

равна для сравнения:

((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))

Не Равно сравнение: просто перечеркивают равна выше сравнения.

NOT ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))

это многословный? Да, это так. Однако это эффективно, так как он не вызывает никакой функции. Идея состоит в том, чтобы использовать короткое замыкание в предикатах, чтобы убедиться, что оператор equal (=) используется только с ненулевыми значениями, иначе null будет распространяться в дереве выражений.

Comments

    Ничего не найдено.