#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
не станет нулевым. Должно быть, происходит что-то еще.
Однако я бы улучшил ваш триггер следующим образом:
- Используйте только один триггер, каждый триггер имеет накладные расходы, наличие одного триггера с одним оператором обновления будет выполняться быстрее, чем 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 После дополнительного обзора проблема была связана с инструментом, используемым для редактирования, и с тем, как он удерживал что-то в строке. Не уверен, что понимаю эту специфику, но после изменений в процессе все работает так, как ожидалось. Спасибо за все советы о правильном и наиболее эффективном способе настройки триггера