【问题标题】:Oracle How can I keep two tables in sync using a trigger that gets activated based on a timestamp field?Oracle 如何使用基于时间戳字段激活的触发器使两个表保持同步?
【发布时间】:2021-09-23 20:22:56
【问题描述】:

我想进行一次性导入/导出以将数据从 TableA 复制到 TableB。但是,在一次性导入数据后,我需要使用触发器使表保持同步,直到我们准备好使用新表。

是否可以编写一个触发器,根据 tableA 中时间戳字段的值将数据从一个表复制到另一个表,或者在 systimestamp 上使用条件。属性上的触发器是首选,但如果不可能,那我可以去 systimestamp 吗?

我是 Oracle 的新手,如果有人能帮我想出这样的触发器,我将不胜感激。

TableA
CREATE TABLE user.tableA (
    attr1 ... NOT NULL,
    attr2 ...,
    ...,
    attrN TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT tableA_PK PRIMARY KEY (attr1) ENABLE
 )

user.tableB is the same as user.tableA

需要想出一个触发器来将插入从 TableA 复制到 TableB。 下面的代码需要工作

CREATE TRIGGER user.sync_from_A_to_B
  AFTER INSERT ON user.tableA
  FOR EACH ROW
BEGIN
  if (systimestamp OR attrN >= to_timestamp('2021-07-31 00:00:00', 'YYYY-MM-DD HH24:MI:SS')
  then
    INSERT INTO user.tableB
         (
             attr1,
             attr2,
             ...,
             attrN
         )
         VALUES
         (
             :new.attr1,
             :new.attr1,
             :new....,
             :new.attrN
         )
  end if;
END;
/

【问题讨论】:

  • 为什么需要检查时间戳? INSERT 触发器只会对当前正在插入 的行触发。这是一个新行,所以只需将其复制到 tableB。另一种解决方案是让 tableB 成为 tableA 的物化视图。
  • 物化视图应该完全满足您的需要
  • 新表有什么不同?您可能可以使用 dbms_redefinition 从一个表结构迁移到另一个表结构 - 这允许您将列映射在一起并让 Oracle 保证在将新表换成旧表之前同步表。如果您想保留两个表,则物化视图适用于,这听起来不像您所追求的。

标签: sql oracle plsql triggers


【解决方案1】:

使用本机功能同步两个表的便捷方法是创建materialized view,并在提交时快速刷新。本质上,它是一个表和查询定义来填充该表。然后,Oracle 在刷新时更新目标表(在您的情况下为on commit)。然后您可以删除物化视图并保留表(因此删除查询定义和对基表的任何依赖)以将您的应用程序重新定位到它。

SQL> set echo on
SQL> 
SQL> create table t_a (
  2    id primary key,
  3    val,
  4    str
  5  ) as
  6  select level, level, chr(32 + level)
  7  from dual
  8  connect by level < 4;

Table T_A created.
 
SQL> select * from t_a;

        ID        VAL STR 
---------- ---------- ----
         1          1 !   
         2          2 "   
         3          3 #   
 
SQL> create materialized view log on t_a
  2  with primary key /*To enable incremental updates*/;

Materialized view log T_A created.

SQL> create materialized view t_b
  2  refresh on commit fast
  3  as
  4  select * from t_a;

Materialized view T_B created.

SQL> select * from t_b;

        ID        VAL STR 
---------- ---------- ----
         1          1 !   
         2          2 "   
         3          3 #   

SQL> insert into t_a values(10, 11, 'W');

1 row inserted.

SQL> update t_a set val = 100 where id = 1;

1 row updated.

SQL> select * from t_b;

        ID        VAL STR 
---------- ---------- ----
         1          1 !   
         2          2 "   
         3          3 #   

SQL> /*Still no changes*/ 
SQL> commit;

Commit complete.
 
SQL> select * from t_b;

        ID        VAL STR 
---------- ---------- ----
         2          2 "   
         3          3 #   
         1        100 !   
        10         11 W   
 
SQL> /*Looks ok, we're moving to target table*/
SQL> drop materialized view t_b preserve table;

Materialized view T_B dropped.
 
SQL> drop table t_a;

Table T_A dropped.
 
SQL> select * from t_b;

        ID        VAL STR 
---------- ---------- ----
         2          2 "   
         3          3 #   
         1        100 !   
        10         11 W   

【讨论】:

    【解决方案2】:

    我猜测物化视图无法满足您的需求。确实,它比触发强制同步要好得多,但问题的关键似乎是直到我们准备好使用新表。 这意味着在未来某个时间源表 (tableA) 将被停止(删除?)。如果是这样,那么在那个时候物化视图刷新变得无效。
    但是,您不需要后行触发器,而是后语句触发器。该触发器将直接从 TableA 插入到 TableB。

    create trigger user.sync_from_a_to_b
      after insert on user.tablea 
    begin
        insert into user.tableb
             (
                 attr1,
                 attr2,
                 ...,
                 attrn
             )
          select 
                 attr1,
                 attr2,
                 ...,
                 attrn
           from tablea 
          where attrn >= (select max attrn from tableb);  
    end;
    

    您面临的主要问题是同步还需要处理删除和更新。也许你最好的选择是两阶段方法。创建一个 物化视图,直到您准备好切换到新表。然后一次性直接从 MV 切换到新表。

    【讨论】:

    • 不错。我对物化视图了解不多,也没有太多时间去探索它。但是,我正在查看触发器的语法。触发器上有一个 WHEN 子句。是否可以使用 WHEN 子句来实现上述目标?
    • 仅供参考:我的 TableA 上没有删除和更新
    • 不!至少使用 STATEMENT 级别触发器,其中 WHEN 子句无效。您可以将它与 ROW 级别触发器和硬编码值一起使用(如在您的初始查询中)。我从未尝试过,但我怀疑何时可以选择(您可以编写测试演员表)。但是,如果你一次只做 1 行以外的任何事情,这将有低得多的性能。如果您插入了 1000 行,那么您的触发器必须运行 1000 次。让我建议您没有时间不调查 MV。您是 Oracle 新手,所以这里有一个建议:询问有经验的开发人员或 DBA。
    • 现在我明白了语句和行级触发器之间的区别。但是,TableA 仅具有行级插入。因此,对于语句级和行级插入,它是相同的。我会问 DBA 他是否有任何物化视图的经验。如果我添加一个像 WHEN new.attrN >= to_timestamp('2021-07-19 00:00:00', 'YYYY-MM-DD HH24:MI:SS') 这样的子句,它会使触发器非常慢吗?
    猜你喜欢
    • 2020-05-20
    • 2013-11-25
    • 2021-07-19
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 2017-07-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多