Каков наилучший способ поддерживать столбец LastUpdatedDate в SQL? - PullRequest
7 голосов
/ 08 января 2009

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

  1. Имейте триггер, обновляющий поле.
  2. Попросите программу, которая выполняет вставку / обновление, установить поле.

Первый вариант кажется самым простым, поскольку мне даже не нужно перекомпилировать его, но это не так уж и сложно. Кроме этого, мне трудно думать о каких-либо причинах, чтобы сделать одно над другим. Есть предложения?

Ответы [ 10 ]

7 голосов
/ 08 января 2009

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

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

Если ваше приложение в значительной степени похоже на это, или любое другое приложение получит доступ к базе данных через один и тот же слой данных, то я бы избежал этого кошмара, который триггеры могут вызывать и помещать логику прямо в ваш слой данных (SQL, ORM, хранимые процессы , так далее.).

Конечно, вам нужно убедиться, что ваш источник времени (ваше приложение, компьютеры ваших пользователей, ваш сервер SQL) точен в любом случае.


Относительно того, почему я не люблю триггеры:

Возможно, я был опрометчив, называя их кошмаром. Как и все остальное, они уместны в меру. Если вы используете их для очень простых вещей, как это, я мог бы попасть на борт.

Именно когда код триггера становится сложным (и дорогим), триггеры начинают вызывать множество проблем. Они являются скрытым налогом на каждый выполняемый вами запрос вставки / обновления / удаления (в зависимости от типа триггера). Если этот налог приемлем, то они могут быть подходящим инструментом для работы.

6 голосов
/ 08 января 2009

Вы не упомянули 3. Используйте хранимую процедуру для обновления таблицы. Процедура может устанавливать временные метки по желанию.

Возможно, это неосуществимо для вас, но я не видел, чтобы это упоминалось.

4 голосов
/ 08 января 2009

Пока я использую СУБД, триггерам которой я доверяю, я всегда буду использовать опцию триггера. Это позволяет СУБД позаботиться о максимально возможном количестве вещей, что, как правило, хорошо.

Убедитесь, что при любых обстоятельствах столбец метки времени имеет правильное значение. Накладные расходы были бы незначительными.

Единственное, что было бы против триггеров, это мобильность. Если это не проблема, я не думаю, что есть вопрос, в каком направлении идти.

4 голосов
/ 08 января 2009

Я бы сказал, триггер на тот случай, если кто-то использует что-то помимо вашего приложения для обновления таблицы, вы, вероятно, также захотите иметь LastUpdatedBy и использовать для этого SUSER_SNAME (), чтобы вы могли видеть, кто сделал обновление

3 голосов
/ 08 января 2009

Триггеры - это благословение и проклятие.

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

Проклятие: ты не знаешь, что происходит за твоей спиной. Проблемы / взаимные блокировки параллелизма дополнительными объектами, введенными в транзакции, которые изначально не ожидались. Фантомное поведение, включая изменения среды сеанса, ненадежные счета строк. Чрезмерное срабатывание условий .. дополнительные штрафы за горячую точку / производительность.

Ответ на этот вопрос (Обновление дат неявно (триггер) или явно (код)) обычно имеет большой вес в контексте. Например, если вы используете дату последнего изменения в качестве информационного поля, вы можете изменить ее только в том случае, если «пользователь» действительно вносит существенные изменения в строку по сравнению с автоматизированным процессом, который просто обновляет какой-то внутренний маркер, который не интересует пользователей. .

Если вы используете триггер для синхронизации изменений или у вас нет контроля над кодом, который выполняет триггер, это имеет гораздо больший смысл.

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

Наконец, несколько систем способны выполнить один триггер для нескольких изменений (несколько строк, выполненных в транзакции), ваш код должен быть готов к тому, чтобы применить себя как массовое обновление для нескольких строк.

3 голосов
/ 08 января 2009

Для меня это звучит как бизнес-логика ... Я был бы более склонен поместить это в код. Позвольте базе данных управлять хранением данных ... Не больше и не меньше.

3 голосов
/ 08 января 2009

Я сторонник хранимых процедур для всего. Ваш процесс обновления может содержать GETDATE () для столбца.

И мне не нравятся триггеры для такого рода обновлений. Отсутствие видимости триггеров приводит к путанице.

2 голосов
/ 08 января 2009

Я бы держал дату в базе данных, т. Е. Триггер, хранимую процедуру и т. Д. В большинстве ваших приложений, управляемых базой данных, пользовательское приложение не будет единственным средством, с помощью которого бизнес-пользователи получают данные , Есть инструменты отчетности, выдержки, пользовательский SQL и т. Д. Кроме того, администратор базы данных вносит обновления и исправления, для которых приложение также не будет указывать дату.

Но, честно говоря, причина № 1, по которой я бы не делал этого из приложения, заключается в том, что вы не можете контролировать дату / время на клиентском компьютере. Они могут откатить его назад, чтобы получить больше дней на пробную лицензию, или просто захотеть сделать что-то плохое для вашей программы.

2 голосов
/ 08 января 2009

Обычно я бы сказал, делать это на стороне базы данных, но это зависит от вашего приложения. Если вы используете LINQ-to-SQL, вы можете просто установить поле как Timestamp, и ваш DAL будет использовать поле Timestamp для параллелизма. Он обрабатывает это для вас автоматически, поэтому повторение кода не является событием.

Если вы пишете свой DAL самостоятельно, то я бы с большей вероятностью справился с этим на стороне базы данных, поскольку это делает написание пользовательских интерфейсов гораздо более гибким - хотя, я бы, вероятно, сделал это в хранимой процедуре, которая имеет «публичный» доступ и таблицы заблокированы - вам не нужно, чтобы какой-либо клоун приходил и обходил вашу хранимую процедуру, записывая непосредственно в таблицы ... если вы не планируете сделать свой DAL автономным компонентом, который любое будущее приложение должно используйте для доступа к базе данных, в этом случае вы можете закодировать ее непосредственно в DAL - конечно, вы должны делать это только в том случае, если вы можете гарантировать, что каждый, кто обращается к базе данных, делает это через ваш компонент DAL.

Если вы собираетесь разрешить «публичный» доступ к базе данных для вставки в таблицы, то вам придется использовать триггер, потому что в противном случае любой может вставить / обновить одно поле в таблице, а обновленное поле может никогда не получать обновления.

1 голос
/ 08 января 2009

Вы можете сделать это без триггера, если ваша база данных поддерживает значения по умолчанию для полей. Например, в SQL Server 2005 у меня есть таблица с полем, созданным так:

create table dbo.Repository
   (
    ...
   last_updated     datetime default getdate(),
    ...
   )

тогда код вставки просто оставляет это поле вне списка полей вставки.

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

Наконец:

create trigger dbo.Repository_Upd on dbo.Repository instead of update
as
--**************************************************************************
--   Trigger: Repository_Upd
--    Author: Ron Savage
--      Date: 09/28/2008
--
-- Description:
-- This trigger sets the last_updated and updated_by fields before the update
-- and puts a copy of the updated row into the Repository_History table.
--
-- Modification History:
-- Date        Init  Comment
-- 10/22/2008  RS    Blocked .prm files from updating the history as they
--                   get updated every time the cfg file is run.
-- 10/21/2008  RS    Updated the insert into the history table to use the
--                   d.last_updated field from the Repository table rather
--                   than getdate() to avoid micro second differences.
-- 09/28/2008  RS    Created.
--**************************************************************************
begin
   --***********************************************************************
   -- Update the record but fill in the updated_by, updated_system and
   -- last_updated date with current information.
   --***********************************************************************
   update cr set
      cr.filename           = i.filename,
      cr.created_by         = i.created_by,
      cr.created_system     = i.created_system,
      cr.create_date        = i.create_date,
      cr.updated_by         = user,
      cr.updated_system     = host_name(),
      cr.last_updated       = getdate(),
      cr.content            = i.content
   from
      Repository cr

      JOIN Inserted i
         on (i.config_id = cr.config_id);


   --***********************************************************************
   -- Put a copy in the history table
   --***********************************************************************
   declare @extention varchar(3);
   select @extention = lower(right(filename,3)) from Inserted;

   if (@extention <> 'prm')
      begin
      Insert into Repository_History
         select
            i.config_id,
            i.filename,
            i.created_by,
            i.created_system,
            i.create_date,
            user           as updated_by,
            host_name()    as updated_system,
            d.last_updated,
            d.content
         from
            Inserted i

            JOIN Repository d
               on (d.config_id = i.config_id);
      end
end

Рон

...