在SQL中维护Last Last Date列的最佳方法是什么?

时间:2009-01-07 21:08:45

标签: sql sql-server database sql-server-2005 triggers

假设我有一个数据库表,其中包含上次更新或插入的时间列。哪个更好:

  1. 触发更新字段。
  2. 让正在执行插入/更新的程序设置字段。
  3. 第一个选项似乎是最简单的,因为我甚至不需要重新编译来执行此操作,但这并不是什么大不了的事。除此之外,我很难想出任何理由去做另一个。有什么建议吗?

10 个答案:

答案 0 :(得分:7)

第一个选项可以更强大,因为数据库将维护该字段。这带来了使用触发器的可能开销。

如果将来有其他应用程序通过自己的界面写入此表,我会使用触发器,因此您不会在其他任何地方重复该逻辑。

如果您的应用程序非常多,或者任何其他应用程序将通过相同的数据层访问数据库,那么我将避免触发器可以诱导并将逻辑直接放入数据层中的噩梦(SQL,ORM,存储过程)等)。

当然,您必须确保您的时间源(您的应用程序,用户的PC,SQL服务器)在任何一种情况下都是准确的。


关于为什么我不喜欢触发器:

也许我把他们称为噩梦是轻率的。像其他一切一样,它们适合适度。如果你将它们用于非常简单的事情,我可以加入。

当触发器代码变得复杂(并且昂贵)时触发器开始引起许多问题。它们是您执行的每个插入/更新/删除查询的隐藏税(取决于触发器的类型)。如果该税可以接受,那么它们可以成为工作的正确工具。

答案 1 :(得分:6)

你没有提到 3。使用存储过程更新表。该过程可以根据需要设置时间戳。

也许这对你来说不可行,但我没有看到它提到。

答案 2 :(得分:4)

我会说触发器以防万一有人使用你的应用程序以外的东西来更新表,你可能也想要一个LastUpdatedBy并使用SUSER_SNAME(),这样你就可以看到谁做了更新

答案 3 :(得分:4)

只要我在我信任的触发器中使用DBMS,我总是使用触发器选项。它允许DBMS处理尽可能多的事情,这通常是一件好事。

确保在任何情况下,timestamp列都具有正确的值。开销可以忽略不计。

唯一可能针对触发器的是可移植性。如果这不是一个问题,我认为没有问题的方向。

答案 4 :(得分:3)

我支持所有内容的存储过程。您的更新过程可能包含列的GETDATE()。

我不喜欢这种更新的触发器。缺乏触发器的可见性往往会引起混淆。

答案 5 :(得分:3)

这对我来说听起来像是商业逻辑......我更倾向于把它放在代码中。让数据库管理数据的存储......不多也不少。

答案 6 :(得分:3)

触发器是一种祝福和诅咒。

祝福:您可以使用它们来启用各种自定义约束检查和数据管理,而无需后端系统知识或更改。

诅咒:你不知道背后发生了什么。引入非原始预期事务的其他对象的并发问题/死锁。幻像行为包括会话环境更改,不可靠的行计数。过度触发条件......额外的热点/性能损失。

这个问题的答案(隐式更新日期(触发)或显式(代码))通常会严重影响上下文。例如,如果您使用上次更改日期作为信息字段,您可能只想在“用户”实际对行进行显着更改时更改它而不是自动进程只更新某种内部标记用户不关心

如果您使用触发器进行更改同步,或者您无法控制正在执行触发器的代码,则更有意义。

我对触发器的建议使用它要小心。大多数系统允许您根据操作和更改的字段过滤执行。正确使用“之前”与“之后”触发器会对性能产生重大影响。

最后,一些系统能够在多次更改(在事务中实现多行)上执行单个触发器,您的代码应准备好将其自身应用为多行的批量更新。

答案 7 :(得分:2)

通常情况下我会说它是数据库端,但这取决于你的应用程序。如果您正在使用LINQ-to-SQL,则只需将该字段设置为Timestamp,并让DAL使用Timestamp字段进行并发。它会自动为您处理,因此不得不重复代码。

如果您自己编写DAL,那么我更有可能在数据库方面处理这个问题,因为它使编写用户界面变得更加灵活 - 尽管如此,我可能会在存储过程中执行此操作具有“公共”访问权限并且表格被锁定 - 您不希望任何小丑出现并绕过您的存储过程直接写入表格...除非您计划将DAL作为任何未来应用程序必须的独立组件用于访问数据库,在这种情况下,您可以直接将其编码到DAL中 - 当然,如果您可以保证访问数据库的每个人都通过DAL组件这样做,那么您应该这样做。

如果您要允许“公共”访问数据库以插入表格,那么您将不得不使用触发器,因为否则任何人都可以在表格中插入/更新单个字段,并且更新后的字段可以永远不会更新。

答案 8 :(得分:2)

我会在数据库中维护日期,即触发器,存储过程等。在大多数数据库驱动的应用程序中,用户应用程序不会是业务用户获取数据的唯一方法。有报告工具,摘录,用户SQL等。还有DBA完成的更新和更正,应用程序也不会提供日期。

但老实说,我不会从应用程序中执行此操作的第一个原因是您无法控制客户端计算机上的日期/时间。他们可能会回滚它以获得更多的试用许可证,或者只是想对你的程序做坏事。

答案 9 :(得分:1)

如果数据库支持字段的默认值,则可以在没有触发器的情况下执行此操作。例如,在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

罗恩