Триггер : обновление столбца на основе значения другого столбца

#sql #sql-server #tsql #triggers #sql-update

Вопрос:

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

Например, у нас есть статус «Выключено» и статус «Найдено выключено» вместе с соответствующими столбцами DateOff и DateFoundOff . Я пытаюсь создать триггер для обновления этих столбцов дат при изменении статуса.

Мне это кажется довольно простым, но то, что происходит, происходит, когда Status изменения в соответствующем столбце даты обновляются правильно, но другой столбец даты становится нулевым. Поэтому, если я изменю статус = «Выключено», у даты будет правильная дата, но DateFoundOff станет нулевым, и наоборот.

Я создал два триггера — первый из них:

 ALTER TRIGGER [GIS].[UPDATE_FOUNDOFF]
ON [GIS].[METEROUTAGEPOINTS]
AFTER UPDATE
AS 
    IF (UPDATE (OutageStatus))
    BEGIN
        SET NOCOUNT ON;

        UPDATE [gis].[METEROUTAGEPOINTS] 
        SET DateFoundOff = CURRENT_TIMESTAMP
        FROM gis.METEROUTAGEPOINTS mop 
        INNER JOIN inserted AS i ON i.ConObject = mop.ConObject
        WHERE i.OutageStatus = 'Found Off'
    END
 

И второе

 ALTER TRIGGER [GIS].[UPDATE_DATES]
ON  [GIS].[METEROUTAGEPOINTS]
AFTER UPDATE
AS 
    IF (UPDATE (OutageStatus))
    BEGIN
        SET NOCOUNT ON;

        UPDATE [gis].[METEROUTAGEPOINTS] 
        SET DateOff = CURRENT_TIMESTAMP 
        FROM gis.METEROUTAGEPOINTS mop 
        INNER JOIN inserted AS i ON i.ConObject = mop.ConObject
        WHERE i.OutageStatus = 'Off'
    END
 

Я просто не понимаю, как один триггер изменяет значение столбца даты на null, которое не связано с текущим значением статуса.

Спасибо.

Изменить: Было обнаружено, что проблема не в триггере, а в том, как инструмент, используемый для редактирования данных, что-то удерживал. Не уверен, что понимаю, почему, но, изменив рабочий процесс редактирования, проблема была решена. Помеченный ответ как правильный, основанный на нем, дает гораздо лучший способ написать триггер

Комментарии:

1. Что такое первичный ключ таблицы?

2. И обратите внимание, что ЕСЛИ ОБНОВЛЕНИЕ не приведет к тому, чего вы ожидаете. Если вы хотите что-то сделать ТОЛЬКО тогда, когда значение действительно изменится, вам нужно сверить предыдущую (удаленную) версию с последующей (вставленной) версией.

3. Я предлагаю создать dbfiddle, потому что в его нынешнем виде он не воспроизводим.

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

Ответ №1:

Ни одно из ваших утверждений об обновлении не изменится DateFoundOff или DateOff не станет нулевым. Должно быть, происходит что-то еще.

Однако я бы улучшил ваш триггер следующим образом:

  1. Используйте только один триггер, каждый триггер имеет накладные расходы, наличие одного триггера с одним оператором обновления будет выполняться быстрее, чем 2.
  2. На самом деле вы не проверяете, изменился ли статус, все, что вы проверяете, — это то, что обновление включило этот столбец. Добавление проверки в deleted таблицу фактически проверяет, изменилось ли значение.
 ALTER TRIGGER [GIS].[UPDATE_FOUNDOFF]
ON [GIS].[METEROUTAGEPOINTS]
AFTER UPDATE
AS 
BEGIN
    -- Avoid doing any processing if no rows are updated
    IF NOT EXISTS (SELECT 1 FROM Inserted) RETURN;

    SET NOCOUNT ON;

    IF UPDATE(OutageStatus) BEGIN
        UPDATE [gis].[METEROUTAGEPOINTS] SET
            DateFoundOff = CASE WHEN i.OutageStatus = 'Found Off' AND d.OutageStatus <> 'Found Off' THEN CURRENT_TIMESTAMP ELSE DateFoundOff END
            , DateOff = CASE WHEN i.OutageStatus = 'Off' AND d.OutageStatus <> 'Off' THEN CURRENT_TIMESTAMP ELSE DateOff END
        FROM gis.METEROUTAGEPOINTS mop 
        INNER JOIN inserted AS i ON i.ConObject = mop.ConObject
        INNER JOIN deleted AS d ON d.ConObject = mop.ConObject;
    END;
END;
 

Комментарии:

1. в соответствии со спецификациями Microsoft, не следует ли вам ввести возврат IF (ROWCOUNT_BIG() = 0); в начале этого триггера? docs.microsoft.com/en-us/sql/t-sql/statements/…

2. @dale-k Я попытался включить соединение в таблицу d, хотя и исключил это из моего опубликованного образца. Я настрою как единый оператор, я изначально пробовал подход с одним оператором, но, основываясь на вашем примере, вероятно, был неверным. Я взломал несколько триггеров, думая, что, возможно, это и было причиной моей проблемы. Я изучу все остальное, что может быть настроено в базе данных, чтобы вызвать это

3. Я на самом деле перешел к IF (NOT EXISTS (SELECT 1 FROM inserted)) своим триггерам из-за риска поставить какое-либо утверждение перед ROWCOUNT

4. ..@@rowcount ненадежен при объединении

5. @DaleK После дополнительного обзора проблема была связана с инструментом, используемым для редактирования, и с тем, как он удерживал что-то в строке. Не уверен, что понимаю эту специфику, но после изменений в процессе все работает так, как ожидалось. Спасибо за все советы о правильном и наиболее эффективном способе настройки триггера