3

这在 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 的所有记录,然后为新产品做一个新的插入。

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

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

编辑1:-

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

4

2 回答 2

4

一种选择是创建一个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。你最终会得到更复杂的代码来处理更改。几乎可以肯定,您会更好地弄清楚发生了什么变化,然后只更新这些行。

于 2012-04-10T03:47:51.420 回答
1

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

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

希望它会帮助你。

于 2012-04-10T03:41:23.803 回答