Как сравнить значения, которые могут быть как 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 для каждого столбца?
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 ( ), когда они используются с нулевыми значениями.
равна для сравнения:
((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 будет распространяться в дереве выражений.
вы проверили NULLIF? http://msdn.microsoft.com/en-us/library/ms177562.aspx
Comments