7

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

  1. 让触发器更新字段。
  2. 让执行插入/更新的程序设置该字段。

第一个选项似乎是最简单的,因为我什至不必重新编译即可,但这并不是什么大不了的事。除此之外,我很难想出任何理由去做一个而不是另一个。有什么建议么?

4

10 回答 10

7

第一个选项可能更健壮,因为数据库将维护该字段。这伴随着使用触发器的可能开销。

如果将来您可以让其他应用程序通过它们自己的接口写入此表,我会使用触发器,这样您就不会在其他任何地方重复该逻辑。

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

当然,无论哪种情况,您都必须确保您的时间来源(您的应用程序、您的用户的个人电脑、您的 SQL 服务器)是准确的。


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

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

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

于 2009-01-07T21:14:58.027 回答
6

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

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

于 2009-01-07T23:29:28.037 回答
4

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

它可以确保在任何情况下时间戳列具有正确的值。开销可以忽略不计。

唯一不利于触发器的是可移植性。如果这不是问题,我认为不存在该往哪个方向发展的问题。

于 2009-01-07T21:20:06.597 回答
3

我会说触发器以防万一有人使用除您的应用程序之外的其他东西来更新表,您可能还希望拥有一个 LastUpdatedBy 并为此使用 SUSER_SNAME() ,这样您就可以看到谁进行了更新

于 2009-01-07T21:18:19.250 回答
3

我是所有事情的存储过程的支持者。您的更新过程可能包含该列的 GETDATE()。

而且我不喜欢这种更新的触发器。缺乏对触发器的可见性往往会导致混乱。

于 2009-01-08T00:05:27.353 回答
3

这对我来说听起来像是业务逻辑......我更愿意将其放入代码中。让数据库管理数据的存储……不多也不少。

于 2009-01-08T00:15:07.553 回答
3

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

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

诅咒:你不知道背后发生了什么。附加对象导致的并发问题/死锁带入了最初未预期的事务。幻象行为,包括会话环境更改、不可靠的行数。过度触发条件..额外的热点/性能损失。

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

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

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

最后,一些系统能够对多个更改(在事务中影响多行)执行单个触发器,您的代码应该准备好将自身作为批量更新应用于多行。

于 2009-01-08T00:46:47.553 回答
2

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

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

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

于 2009-01-07T21:23:41.160 回答
2

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

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

于 2009-01-07T23:14:31.700 回答
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

罗恩

于 2009-01-07T23:22:06.287 回答