Запуск и обновление строки в SQL Server после ее обновления

Это лучший способ просто отслеживать изменения в строке базы данных:

ALTER TRIGGER [dbo].[trg_121s] 
ON  [dbo].[121s]
  AFTER UPDATE
AS 
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

-- Insert statements for trigger here

update dbo.[121s]
set modified=getdate()
where id in 
(select distinct ID from Inserted)

END

Таким образом, любое обновление строки в [121s] приведет к обновлению столбца изменено.

Это работает, но я не уверен, что это лучший способ добиться этого.

Я немного запутался в этой строке:

(select distinct ID from Inserted)

... и как он узнает, что получает правильный идентификатор строки.

Спасибо за любое подтверждение / разъяснение,

отметка


person Mark    schedule 17.09.2012    source источник
comment
Использование GetDate() в запросе преследует движущуюся цель, влияет на производительность и может привести к любопытным результатам, например по мере изменения даты. Почти всегда лучше зафиксировать текущую дату / время в переменной, а затем использовать это значение по мере необходимости. Это более важно для нескольких операторов, как для хранимой процедуры. Наиболее частая причина использования GetDate() несколько раз - захват времени начала и окончания длительной операции.   -  person HABO    schedule 17.09.2012
comment
@HABO, за исключением случаев, когда абсолютно критично, чтобы все строки были помечены одной и той же меткой времени обновления, я не думаю, что в этом случае есть серьезная проблема.   -  person Aaron Bertrand    schedule 17.09.2012
comment
Я не знаю точно, но я думаю, что в заявлении getdate () вернет одну и ту же дату / время для каждой строки.   -  person Dumitrescu Bogdan    schedule 17.09.2012
comment
@DumitrescuBogdan Я совершенно уверен, что есть случаи, когда это не так, и это переоценивается для каждой строки, но я признаюсь, что я не проверял, является ли это одним из них.   -  person Aaron Bertrand    schedule 17.09.2012
comment
@AaronBertrand, не поймите меня неправильно, всякий раз, когда я пишу sp, я всегда начинаю с определения некоторых констант (например, времени). Но мне еще предстоит увидеть внутри select / update / insert разные даты из getdate (). Как я уже сказал, я этого точно не знаю. Я займусь расследованием.   -  person Dumitrescu Bogdan    schedule 17.09.2012
comment
@DumitrescuBogdan для простых случаев я считаю, что вы правы, но для некоторых более сложных случаев см. sqlblog.com/blogs/andrew_kelly/archive/2008/02/27/ и sqlblog.com/blogs/andrew_kelly/archive/2008/03/01/ < / а>   -  person Aaron Bertrand    schedule 17.09.2012
comment
@AaronBertrand Ха-ха .. Google такой маленький. Я только что это прочитал. Но если вы проверите это, он пришел к выводу, что его версия глючит.   -  person Dumitrescu Bogdan    schedule 17.09.2012
comment
@DumitrescuBogdan прав, но он не единственный человек на земле, который может написать запрос, который производит такое же поведение - так что это все равно то, о чем вы должны знать, вместо того, чтобы слепо предполагать, что GETDATE () всегда действует как константа.   -  person Aaron Bertrand    schedule 17.09.2012
comment
@AaronBertrand согласился, как я уже сказал, в коде я этого не делаю. А вот на быстрые обновления / вставки sql заморачиваться редко .. Интересный вопрос, почему так происходит. Может быть, потому, что sql нравится выполнять некоторые запросы параллельно, поэтому я предполагаю, что каждый поток будет вызывать свой собственный getdate (). Если что-то заставляет потоки немного не синхронизироваться, они могут получить разные даты.   -  person Dumitrescu Bogdan    schedule 17.09.2012
comment
Я думаю, что параллелизм - это один случай, но пример UDF - это другой, где даже однопоточный вызов функции будет переоценен, поскольку вы по существу превратили запрос в курсор (и скрыли эту константу от SQL Server).   -  person Aaron Bertrand    schedule 17.09.2012
comment
@AaronBertrand - в этом случае OP желает отслеживать изменения в строках. Потратив немало времени на работу с системами промышленной автоматизации, я стал довольно чувствительным к тому, как люди (неверно) интерпретируют данные. Если вы не можете различить порядок, в котором произошли события, лучший способ действий - применить ко всем одинаковую дату / время. В противном случае кто-то определит, что более раннее событие должно вызвать более позднее.   -  person HABO    schedule 17.09.2012
comment
@HABO в принципе, я согласен, и я даже обратился к этому в своем ответе, но можете ли вы продемонстрировать случай, когда это произойдет в простом обновлении в триггере (где не задействованы UDF)?   -  person Aaron Bertrand    schedule 17.09.2012


Ответы (3)


inserted - это псевдотаблица, и она определенно содержит все нужные строки, на которые повлиял оператор UPDATE (и я предполагаю, что DISTINCT не требуется, если ID первичный ключ, хотя трудно сказать, что это за таблица с таким именем, как 121s). Все ли они действительно имели измененные значения - это еще один вопрос, который вы можете проверить перед применением измененной даты / времени. Если бы не это, я бы, наверное, сделал это так:

ALTER TRIGGER [dbo].[trg_121s] 
ON [dbo].[121s]
AFTER UPDATE
AS 
BEGIN
  SET NOCOUNT ON;

  UPDATE t SET modified = CURRENT_TIMESTAMP
   FROM dbo.[121s] AS t
   WHERE EXISTS (SELECT 1 FROM inserted WHERE ID = t.ID);
END
GO

Если вы хотите иметь 100% надежную гарантию того, что все они обновляются с одной и той же меткой времени (хотя я не знаю, видел ли я когда-либо несколько значений в этом варианте использования):

ALTER TRIGGER [dbo].[trg_121s] 
ON [dbo].[121s]
AFTER UPDATE
AS 
BEGIN
  SET NOCOUNT ON;

  DECLARE @ts DATETIME;
  SET @ts = CURRENT_TIMESTAMP;

  UPDATE t SET modified = @ts
   FROM dbo.[121s] AS t
   WHERE EXISTS (SELECT 1 FROM inserted WHERE ID = t.ID);
END
GO

И если вы хотите, чтобы обновление происходило только в том случае, если, скажем, столбец foo изменил значение, вы можете сказать:

  UPDATE t SET modified = @ts
   FROM dbo.[121s] AS t
   INNER JOIN inserted AS i
   ON t.ID = i.ID
   AND t.foo <> i.foo;

Это становится более сложным, если foo допускает значение NULL, но это общий шаблон.

person Aaron Bertrand    schedule 17.09.2012
comment
Спасибо - я пометил это как ответ, как я знаю из вашего исходного комментария, вы, должно быть, сразу начали работать над этим. Я очень ценю и другие ответы, и поставил им +1 - так что спасибо всем - помощь здесь невероятная! - person Mark; 17.09.2012
comment
Просто быстрый запрос, чтобы расширить свой ответ, если вы не возражаете ... Я видел еще одну ссылку на обновление метки времени, только если определенные столбцы (может быть больше, чем один, как в вашем примере) изменены: if обновление ([имя]) или обновление ([адрес]) начало .... это возможно? (для информации 121s - это таблица личных встреч с персоналом, чтобы дать обновленную информацию о производительности и т. д.) - еще раз спасибо, Марк - person Mark; 17.09.2012
comment
@fixit нет, на это нельзя полагаться. Если я скажу, что UPDATE foo SET bar = bar или bar уже 1, и я скажу UPDATE foo SET bar = 1, тогда UPDATE(bar) вернет истину, даже если я не изменил значение. - person Aaron Bertrand; 17.09.2012
comment
спасибо - так что, если вы хотите проверить два столбца, вы бы установили два триггера? - person Mark; 17.09.2012
comment
@fixit абсолютно нет. AND (t.foo <> i.foo OR t.bar <> i.bar) - person Aaron Bertrand; 17.09.2012
comment
Это нормально для решения - person MediSoft; 10.08.2015

Вставленный - это таблица, содержащая строки, затронутые операцией, запустившей триггер (вставка / обновление). Итак, ваш триггер здесь правильный. Если Id является первичным ключом, вам не нужен отдельный (достаточно выбрать идентификатор из Inserted). Если Id не является первичным ключом, то ваш триггер ложный, так как вы можете в конечном итоге обновить больше, чем должны.

person Dumitrescu Bogdan    schedule 17.09.2012

Хотя многие, не только в Stackoverflow, но и во всем мире, скажут вам, что триггеры - зло, я скажу, что они имеют ценность, когда ими не злоупотребляют. Здесь, если вы хотите использовать этот триггер, он кажется допустимым, и таблица Inserted содержит строки, которые были обновлены оператором, запустившим этот триггер - это правильно, за исключением того, что DISTINCT, вероятно, может быть удален, если ID является первичным ключом.

Однако другой вариант, если у вас есть гибкость, - использовать вместо этого столбец timestamp. Но не запутайтесь, столбец timestamp не коррелирует с датой и временем. Так что, если вам нужна дата и время, придерживайтесь того, что там есть.

person Mike Perrenoud    schedule 17.09.2012
comment
Использование метки времени на самом деле противоположно тому, что я предлагаю. Обычно людям приходится отказываться от отметки времени, потому что на самом деле им нужна дата / время, а не какое-то нечитаемое двоичное значение версии строки, которое не помогает им отследить, когда строка вообще была изменена. - person Aaron Bertrand; 17.09.2012
comment
Мне известно о метке времени - я думаю, что это просто плохо сформулировано - я знаю, что в будущем она будет обесцениваться. - person Mark; 17.09.2012