【问题标题】:DB2 for i Triggers - Once per statement, but comparing before and after valuesDB2 for i 触发器 - 每个语句一次,但比较前后值
【发布时间】:2019-08-12 16:52:41
【问题描述】:

在 DB2 for IBM i 中,是否可以创建一个触发器,该触发器每个语句运行一次,但能够遍历所有受影响的行以确定是否有任何值实际更改?

用例是使用基础表上的触发器更新物化查询表 (MQT)。但我们只想在值实际发生变化时刷新 MQT。如果我们创建每行运行一次的触发器,我们就能获得所需的功能。但是,如果我们一次插入、更新和/或删除多行,则“刷新”语句会针对每一行运行。目前,我们系统上的这个 MQT 大约需要 10 秒。

只是希望有一种方法可以使用触发器集在每个语句中运行一次,但仍然可以访问每个单独行的前后值。我无法找到任何表明它是否可能的东西。虽然我怀疑不是。

如果有其他方法可以有效地自动刷新 MQT,我也愿意。

【问题讨论】:

    标签: db2 database-trigger db2-400 materialized-views


    【解决方案1】:

    是的,文档是here。但是,根据您尝试执行此操作的方式,IBM i 命令 ADDPFTRG 和 SQL CREATE TRIGGER 之间存在一些差异。

    ADDPFTRG 似乎不支持语句级触发器,但 CREATE TRIGGER 支持。

    当使用语句级触发器时,您可以使用

    引用受影响的行
    REFERENCING OLD TABLE AS ___
                NEW TABLE AS ___
    

    您可以使用 SQL 语句处理旧表和新表引用,就像它们是常规表一样。

    为了清楚起见,OLD TABLE 引用受影响的行,因为它们在 SQL 语句运行之前,而 NEW TABLE 引用受影响的行,因为它们将在 SQL 语句之后完成。

    【讨论】:

    • 谢谢@jmarkmurphy。所以我已经通读了该文档,但我没有发现直观的是,如果我使用FOR EACH STATEMENT 而不是FOR EACH ROW,我如何仍然遍历每个受影响的行以便我可以比较之前和在价值观之后?
    • 感谢您尝试澄清。但我不确定我是否了解如何遍历每一行。我会根据我在定义中创建的别名打开游标吗?那么T1 上的光标是否定义为REFERENCING OLD TABLE AS T1...
    • 不要将其视为迭代。分组思考。将它们连接在一起并选择具有差异的行集。如果返回任何差异,您将更新您的 MQT,如果集合返回为空,则不要更新 MQT。
    • 但是,您仍然可以大量刷新 MQT。看来,如果您想保持同步,MQT 可能不是要走的路,而是视图或表函数。
    • 好的,所以OLD TABLENEW TABLE 与相关表共享相同的结构,但只包含受影响的记录,没有其他内容。我想我现在明白了。我在想这些引用代表整个表格,而不仅仅是受影响的行。
    【解决方案2】:

    例子:

    create table test (id int not null primary key, a int)@
    create table test_mqt (cnt) as (select sum(a) from test) data initially deferred refresh deferred maintained by user@
    
    insert into test values (1, 1), (2, 1), (3, 1) with nc@
    
    create or replace trigger test_aus 
    after update on test
    referencing 
    new table as n
    old table as o
    for each statement
    mode db2sql
    begin atomic
      if (exists (select 1 from n,o where n.id=o.id and n.a<>o.a)) then
        refresh table test_mqt;
      end if;
    end@
    
    -- trigger IS NOT fired after the following update
    update test set a=1 with nc@
    -- the following select returns 0
    select cnt from test_mqt@
    
    -- trigger IS fired after the following update
    update test set a=2 with nc@
    -- the following select returns 6
    select cnt from test_mqt@
    

    【讨论】:

    • 好的,所以对 OLD TABLENEW TABLE 的引用一次代表一行,但总是会循环遍历每个受影响的行,而不需要明确的迭代结构?
    • no 这里是转换 tables(受影响行的新旧集)的相关名称,而不是单个受影响的 rows。 “循环遍历每个受影响的行”由触发器定义中的 SQL select 语句进行。
    • 我认为让触发器运行刷新没有意义......请参阅我的答案以获取替代方案。
    • @Charles 我的示例主要是关于如何使用for each statement 触发器检测表更改并在检测到它们时运行一些语句。至于带触发器的真正 MQT 维护 - 它可能是更复杂的解决方案。显然,我们需要after insertafter delete 触发器并重写after update 触发器,如果​​我们在某些时候不使用完全刷新,并且想要增量维护我们的MQT。
    【解决方案3】:

    我认为让触发器运行刷新没有意义......

    触发器 + MQT 的全部意义在于让触发器直接更新 MQT 表。这样,您只需处理已更改的确切行;而不是强制数据库再次读取整个基表。

    create or replace trigger test_aus 
    after update on test
    referencing 
    new row as n
    old row as o
    for each row
    mode db2sql
    begin atomic
      if n.a <> o.a then
        update test_mqt set cnt = cnt + n.a - o.a;
      end if;
    end@
    

    但请注意,如果您只对 a 的更改感兴趣,那么您可以将触发器定义为

    after update of a on test 
    for each row 
    

    这样触发器只会在 a 实际更新时触发。

    如果一次更新的 SQL 集比 RPG(或 SQL)单行更新多,那么语句触发器可能是值得的...

    类似这样的:

    create or replace trigger test_aus 
    after update on test
    referencing 
    new table as n
    old table as o
    for each statement
    mode db2sql
    begin atomic
       update test_mqt 
         set cnt = cnt + (select sum(n.a - o.a) 
                            from N join O using(id)
                            where n.a <> o.a
                          );
    
    end@
    

    编辑:MQT 的替代方案
    我从未在生产中真正使用过 MQT,因为 IBM i 不支持系统维护的 MQT。

    在您对更新的聚合感兴趣的情况下,包含聚合的编码矢量索引 (EVI) 可能会提供更好的解决方案;因为它们是自动维护的。

    CREATE ENCODED VECTOR INDEX sales_fact_location_id_evi 
    ON sales_fact(sale_location_id ASC) 
    INCLUDE(SUM(sale_amount_measure))
    

    上面的例子来自文章Accelerated analytics - faster aggregations using the IBM DB2 for i encoded vector index (EVI) technology。这是一篇很好的文章,并将 EVI 与 MQT 进行了比较以存储聚合。

    【讨论】:

    • 至于带触发器的真正 MQT 维护。不幸的是,DB2 for IBM i 仍然没有 refresh immediaterefresh deferred 与增量维护 MQT,例如 Db2 for LUW 就有。因此,即使对于这样一个最简单的情况,我们也需要after insertafter delete 触发器。但是当事情变得稍微复杂一些时,这样的触发逻辑可能很快就会变得过于复杂,我们最终可能会决定最终采用一种解决方案,比如在某个时候触发完全刷新。
    • @MarkBarinstein 同意很遗憾 Db2 for i 不支持系统维护的 MQT。但是如果您需要实时更新 MQT,那么更新 MQT 的触发器是唯一的选择。如果不需要实时,那么计划刷新(使用刷新语句或用户程序)就可以了。让触发器使用刷新命令没有意义。
    猜你喜欢
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多