Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Условие T-SQL IF не работает должным образом

Автор вопроса: JedОтветов: 4

У меня есть хранимая процедура, которая будет ВСТАВЛЯТЬ запись в таблицу. Я создал уникальное ограничение ключа для таблицы, чтобы избежать дублирования записей.

Независимо от того, что определено уникальное ограничение ключа, я использую условие IF() (перед оператором INSERT) для проверки наличия повторяющейся записи, которая может уже существовать.

Однако условный оператор, который я использую для проверки дублирующейся записи, по-видимому, не влияет на то, выполняется ли INSERT. - т. е. когда повторяющаяся запись отправляется в sproc, возникает исключение "Нарушение ограничения UNIQUE KEY...".

Вот пример моего sproc:

BEGIN
    if
        (SELECT Count(f1)
        FROM table
        WHERE f1 = @f1
        AND f2= @f2)
    <= 0
    BEGIN
        INSERT INTO table
        (f1,f2)
        VALUES
        (@f1, @f2)

        RETURN @@IDENTITY
    END
END

Что-то не так с моим синтаксисом? Или, может быть, я иду об этом неправильно?

  • Это при высокой нагрузке? Две параллельные транзакции могут прочитать, что строка не существует, а затем перейти к этапу вставки. Связанный вопрос Также не относится к вашему вопросу, но вы следует использовать scope_identity, если цель состоит в том, чтобы вернуть идентификатор самой последней вставленной строки. Martin Smith 02.10.2010

sqlif-statementsql-servertsqlsql-server-2008

Ответы

Ответ 1 · рейтинг 3 · LukeH

Если вы используете SQL Server 2008 или новее, вам следует использовать MERGE заявление:

MERGE INTO table AS t
USING (VALUES (@f1, @f2)) AS s (f1, f2)
    ON s.f1 = t.f1
        AND s.f2 = t.f2
WHEN NOT MATCHED BY t THEN
    INSERT (f1, f2) VALUES (@f1, @f2)

Ответ 2 · рейтинг 3 · GSerg

Count(field) подсчитывает ненулевые значения.
Это может отличаться от count(*), который подсчитывает строки.

Но я бы не советовал использовать здесь count.
Взгляните на этот вопрос.

Ответ 3 · рейтинг 2 · OMG Ponies

Это более безопасно для параллелизма:

INSERT INTO table
SELECT @f1, @f2, @f3
  FROM TABLE WITH (UPDLOCK, HOLDLOCK)
 WHERE NOT EXISTS(SELECT NULL
                    FROM table
                   WHERE f1 = @f1
                     AND f2 = @f2
                     AND f3 = @f3)

Или, если вы хотите сохранить логику принятия решения (но менее защищенную от параллелизма), используйте НЕ СУЩЕСТВУЕТ:

IF NOT EXISTS(SELECT NULL
                FROM table
               WHERE f1 = @f1
                 AND f2 = @f2
                 AND f3 = @f3)
BEGIN

    INSERT INTO table
      (f1,f2, f3)
    VALUES
      (@f1, @f2, @f3)

    RETURN @@SCOPE_IDENTITY

END

Ответ 4 · рейтинг 1 · Jed

Спасибо за ваш вклад OMG/Martin, но оказалось, что корень моей проблемы был из-за значения параметра, которое было нулевым.

Конкретно:

--sproc params
@f1 int,
@f2 nvarchar(30),
@f3 datetime = null --<<a null value will screw up the IF() condition

IF(SELECT COUNT(f1)
FROM table
WHERE f1 = @f1
AND f2 = @f2
AND f3 = @f3)
<1

BEGIN
  INSERT INTO....
END

Обратите внимание, что значение параметра @f3 по умолчанию равно null. Итак, в случае, когда вызывающая сторона sproc не передает параметр @f3, условие IF() вернет 0 (ноль), даже если есть соответствующие @f1, @f2 и null @f3.

Например: скажем, в таблице уже есть запись

f1   f2   f3
--------------
45   foo  NULL

Теперь вызывающий объект запускает sproc, отправляя его:

@f1=45

@f2=фу

(Обратите внимание, что вызывающий не указывает @f3)

Когда условие IF() сработает, оно вернет 0 (ноль). Странно, а? Интуитивно я бы подумал, что условие вернет 1, поскольку параметры точно совпадают с существующими значениями в таблице.

Чтобы сделать ситуацию немного более запутанной (для меня, во всяком случае), это то, что даже если IF () завершается успешно (возвращает ноль), я получаю исключение "Нарушение ограничения UNIQUE KEY..". На самом деле, я не удивлен, что возникает исключение, поскольку я считаю, что значения параметров идеально соответствуют существующей записи в таблице - для меня запутанная часть заключается в том, почему условие IF() противоречит исключению нарушения .

Кстати, OMGPonies, я попробовал использовать ваше предложение (СУЩЕСТВУЕТ), и возникают те же симптомы. По-видимому, фактор NULL делает вещи странными.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы