自动更新数据库中的字段

时间:2009-09-22 11:17:25

标签: oracle triggers view ora-04091

我想使用另一列的聚合自动更新数据库列。 涉及三个表:

T_RIDER
  RIDER_ID
  TMP_PONYLIST
  ...

T_RIDER_PONY
  RIDER_ID
  PONY_ID

T_PONY
  PONY_ID
  PONY_NAME
  ...

T_RIDERT_PONY通过T_RIDER_PONY建立了 n:m 关系。 T_RIDERT_PONY有更多列,但此处只有TMP_PONYLISTPONY_NAME

TMP_PONYLISTPONY_NAMES的分号spararated列表,想象"Twisty Tail;Candy Cane;Lucky Leaf"之类的内容。 无论T_RIDER_PONYT_PONY发生什么情况,我都希望保持此字段为最新状态。

我的第一个想法是在T_RIDER_PONYT_PONY上设置触发器。 问题是,似乎无法在触发器中读取T_RIDER_PONY,我总是得到ORA-04091。我找到了一些关于使用三个触发器和包变量的提示,但这听起来太复杂了。

也许您认为我最好更改架构或完全摆脱TMP_PONYLIST。 这些是选项,但不是这个问题的主题。 目前我只对那些不需要对我的应用程序进行任何更改的答案感兴趣(没有应用程序直接使用表,只允许使用视图,因此允许使用视图进行欺骗)。

那么,如何让TMP_PONYLIST自动更新? 如何连接字符串是一个有趣的子问题我还没有找到一个优雅的解决方案。

我正在使用Oracle数据库10g企业版10.2.0.4.0 - 64位。

更新

我喜欢使用物化视图的想法。 我所拥有的是:

CREATE MATERIALIZED VIEW
  V_TMP_PONYLIST 
BUILD IMMEDIATE
REFRESH COMPLETE ON COMMIT
AS SELECT 
  R.RIDER_ID, string_agg(P.PONY_NAME) AS TMP_PONYLIST
FROM
  T_PONY P, T_RIDER R, T_RIDER_PONY RP
WHERE 
  P.PLOTGROUP_ID=RP.PLOTGROUP_ID AND
  R.QUEUE_ID=RP.QUEUE_ID 
GROUP BY R.RIDER_ID;

string_agg未显示,因为它很长,我认为它不相关。

不会使用ON COMMIT进行编译,我得到ORA-12054。 据我所知,文档聚合仅被禁止 REFRESH FAST,那么这里的问题是什么?

更新 Vincents和Tonys的答案不同但都很有帮助。 我接受了托尼,但一定要读文森的回答。

2 个答案:

答案 0 :(得分:3)

TMP_PONYLIST上的信息是多余的(它存在于其他地方)。您将遇到各种问题以维护它(除非存在某种锁定机制,否则没有解决方案可以在多用户环境中正常工作)。

在标准化模型中,您只需从物理模型中删除此列。如果您需要这些信息,可以使用视图,例如Oracle 11gR2:

CREATE OR REPLACE VIEW rider_v AS
SELECT rider_id, /*...,*/
       (SELECT listagg(p.pony_name, ';') WITHIN GROUP (ORDER BY p.pony_name)
          FROM t_pony p
          JOIN t_rider_pony rp ON (p.pony_id = rp.pony_id)
         WHERE rp.rider_id = r.rider_id) tmp_ponylist
  FROM t_rider r;

请参阅this SO,了解11gR2之前的字符串聚合。

答案 1 :(得分:1)

为什么需要读取触发器中的T_RIDER_PONY表?您将插入或删除一行,因此所有触发器需要做的是在T_PONY中查找小马名称并更新表T_RIDER,或者将名称附加到TMP_PONYLIST或从TMP_PONYLIST中删除它

create trigger t_rider_pony_trg 
after insert or delete on t_rider_pony
for each row
begin
   if inserting then
      select pony_name
      into   l_pony_name
      where  pony_id = :new.pony_id;

      update t_rider
      set    tmp_ponylist = tmp_ponylist || ';' || l_pony_name
      where  rider_id = :new.rider_id;
   elsif deleting then
      select pony_name
      into   l_pony_name
      where  pony_id = :old.pony_id;

      update t_rider
      set    tmp_ponylist = ltrim ( 
                               rtrim ( 
                                  replace(';' || tmp_ponylist || ';',
                                          ';' || l_pony_name || ';',
                                          ';'),
                                  ';', ';')
      where  rider_id = :old.rider_id;
   end if;
end;

我承认删除部分的更新是相当不愉快的;最好使用像apex_util.string_to_table和apex_util.table_to_string这样的实用程序来处理它!有关详细信息,请参阅this SO answer