【问题标题】:How to keep track of historical changes to reference table?如何跟踪参考表的历史变化?
【发布时间】:2012-04-10 03:32:30
【问题描述】:

这在 Web 应用程序中是很常见的事情。如果我有一个用户表并且我想跟踪对用户表所做的所有更改,我可以使用数据库插入和更新触发器将这些更改保存在 user_history 表中。

但是如果我有 user_products 表,我有 user_id 、 product_id 和 cost。当我在系统中添加用户时,假设我有两个产品与该用户关联。因此,我的 user_products 表将为该用户提供两行。

user_id product_id cost
1       10         1000
2       20         2000

现在如果我进入编辑用户页面并删除产品 1 ,添加产品 3 ,将产品 2 的成本从 2000 更改为 3000。

所以通常我会从 user_product 表中删除 user_id 1 的所有记录,然后为新产品进行新的插入。

所以它不是定期更新,而是删除然后插入。因此,我无法跟踪历史更改。

理想情况下,我想知道我删除了产品 1,添加了产品 3,将产品 2 的成本从 2000 更改为 3000。

编辑 1:-

我没有更新。我正在删除然后插入。所以我正在删除产品 ID 为 2 且成本为 2000 的记录。然后再次插入产品 ID 为 2 但成本为 3000 的记录。所以从技术上讲,它的删除和插入但逻辑上只有成本从 2000 更改为 3000。如果我在执行两者时检查查询它会说我删除了 id 为 2 的产品,然后添加了相同的 id 为 2 的产品。但我希望能够看到成本从 2000 变为 3000

【问题讨论】:

  • 为什么不直接更新而不是删除和插入?
  • @Dan A. 它基本上是从用户到产品的一对多关系。用户可以拥有 100 多种产品。如果用户现在有 10 个产品。他以后可能有40种产品。如果我向用户添加新产品,我将不得不检查该用户产品映射是否存在于表中。如果是,则更新,如果不是,则插入。所以对我来说很容易删除所有内容然后再次插入所有产品。这样我就不必检查现有记录和新记录并相应地运行插入或更新查询。
  • 当您尝试跟踪历史记录时,您从不检查是否存在要更新的产品中获得的任何便利都会丢失。在我看来,通过实际执行“技术更新”来执行“逻辑更新”是更好的做法。如果您决定在产品记录中添加“create_date”和“last_updated”列怎么办?如果您更新现有记录,这很简单。

标签: sql oracle history


【解决方案1】:

一种选择是创建一个user_product_history 表,该表由user_product 上的触发器填充,然后定义一个触发器,将历史表中的旧“删除”行转换为update,如果该行随后是插入。

CREATE TABLE user_product_history (
  user_id         number,
  product_id      number,
  cost            number,
  operation_type  varchar2(1),
  operation_date  date
);

CREATE TRIGGER trg_user_product_history
  AFTER INSERT OR UPDATE OR DELETE ON user_product
  FOR EACH ROW
DECLARE 
  l_cnt integer;
BEGIN
  IF( deleting )
  THEN
    insert into user_product_history( user_id, product_id, cost, operation_type, operation_date )
      values( :old.user_id, :old.product_id, :old.cost, 'D', sysdate );
  ELSIF( updating )
  THEN
    insert into user_product_history( user_id, product_id, cost, operation_type, operation_date )
      values( :new.user_id, :new.product_id, :new.cost, 'U', sysdate );
  ELSIF( inserting )
  THEN
    select count(*)
      into l_cnt
      from user_product_history
     where operation_type = 'D' 
       and user_id        = :new.user_id
       and product_id     = :new.product_id;
    if( l_cnt > 0 )
    then
      update user_product_history
         set operation_type = 'U',
             operation_date = sysdate,
             cost           = :new.cost
       where operation_type = 'D' 
         and user_id        = :new.user_id
         and product_id     = :new.product_id;
    else
      insert into user_product_history( user_id, product_id, cost, operation_type, operation_date )
        values( :new.user_id, :new.product_id, :new.cost, 'I', sysdate );
    end if;
  END IF;
END;

但是,从效率的角度来看,执行删除和插入而不是更新意味着您在数据库上施加的负载远远超过了必要的负载。您将执行比必要更多的 I/O。你最终会得到更复杂的代码来处理更改。几乎可以肯定,您最好弄清楚发生了什么变化,然后只更新这些行。

【讨论】:

  • 感谢您的回答。看到我已经更新了我的问题。这个触发器和表格在我的情况下是否有效?
  • @Ashish - 我发布了一个示例,说明如何调整触发器以将删除后的插入视为历史表中的逻辑更新。但是,您最好从一开始就进行更新。
  • 尝试将插入转换为历史表中的更新的另一个缺点是,您无法真正区分真正的更新和稍后再插入的实际删除。跨度>
  • @贾斯汀洞穴。现在我已经准备好了审计日志表。我如何向用户展示这个。我的意思是审计表有像 product_id、user_id 等的 id。我如何将它们加入到各自的表中并获得像 user anme 和 product name 这样的名称?请帮忙。
  • @Ashish - 我不确定我是否理解这个问题......大概,你会加入USER 表和PRODUCT 表,就像你查询时一样USER_PRODUCT 表。 SELECT * FROM user_product_history JOIN user USING (user_id) JOIN product USING (product_id)
【解决方案2】:

我不知道是否有“标准”方式或简单方式,但根据我的经验,你必须自己做...... 当然,您不需要为每个表编写一个特定的方法,而是一个处理所有更新 SQL 的通用方法,根据您的表结构,这很聪明。 例子: 您可以定义一个历史表,该表将具有: id - 表名 - 表记录 id - 要更新的字段列表 - BEFORE 更新的值列表 - AFTER 更新的值列表

然后,在您的代码中,您应该有一个处理 SQL 查询的抽象类:在更新时,您调用将解析 SQL 查询并将记录插入此表的方法,即使您必须查询 SELECT在更新之前检索数据(开销最小,因为 SELECT 只使用很少的资源,您可以使用数据库服务器缓存)。 最后,当您显示与记录相关的信息时,假设是用户,您可以添加代码以显示此表上的历史记录以及此用户 ID。

希望对你有所帮助。

【讨论】:

  • 感谢您的回答。但我不做更新。我正在删除然后插入。所以我正在删除产品 ID 为 2 且成本为 2000 的记录。然后再次插入产品 ID 为 2 但成本为 3000 的记录。所以从技术上讲,它的删除和插入但逻辑上只有成本从 2000 更改为 3000。如果我在执行两者时检查查询它会说我删除了 id 为 2 的产品,然后添加了相同的 id 为 2 的产品。但我希望能够看到成本从 2000 变为 3000
  • 但我提到的解决方案是相同的:当我写“更新”时,我想说的是“插入/更新/删除”。为了为您完成解决方案,我建议您为操作插入另一列:update/insert/delete。然后进入该方法,您可以轻松地填写此列,因为 SQL 查询以选择、插入、更新或删除开头……这就是我现在的应用程序所拥有的!它工作正常。当然,我的history还有user_id、date、要执行的sql等字段更多...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-14
  • 1970-01-01
  • 2020-11-07
相关资源
最近更新 更多