【问题标题】:Automatically Update Field in Database自动更新数据库中的字段
【发布时间】:2010-11-30 09:01:30
【问题描述】:

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

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 的分号分隔列表,想象一下"Twisty Tail;Candy Cane;Lucky Leaf"。 无论T_RIDER_PONYT_PONY 发生什么情况,我都希望此字段保持最新状态。

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

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

那么,我怎样才能让TMP_PONYLIST 自动保持最新状态? 如何连接字符串是一个有趣的子问题,我还没有找到一个优雅的解决方案。

我使用的是 Oracle Database 10g 企业版 10.2.0.4.0 - 64bi。

更新

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

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 的答案不同,但都有帮助。 我接受了托尼,但也请务必阅读文森特的回答。

【问题讨论】:

  • 我建议不要在单个列中存储多个值(“Twisty Tail;Candy Cane;Lucky Leaf”)...

标签: oracle triggers view ora-04091


【解决方案1】:

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 之前的字符串聚合示例。

【讨论】:

  • TMP_PONYLIST 是出于性能原因而引入的。我们可能会摆脱 TMP_PONYLIST,但我仍在探索其他选项。我喜欢使用视图的想法,但它必须是物化的。请参阅我对问题的更新。
【解决方案2】:

为什么需要读取触发器中的 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

【讨论】:

  • +1 很酷的解决方案,我认为字符串操作太复杂了,但您的解决方案非常好。目前 tmp_ponylist 已排序。您认为扩展您的解决方案以保持 tmp_ponylist 排序会很困难吗?
  • 可以对列表进行排序,但我认为您需要使用临时表或用户定义的类型来存储它并按顺序从中选择。或者自己编写一些冒泡排序代码来处理 PL/SQL 集合!
  • 我认为这不适用于多用户环境。用户 A 插入一行触发器更新 T_Rider;用户 B 插入一行,没有看到 A 的变化; A 阻止 B 的更新; A 提交,释放锁,B 更新; B 提交,A 的更改消失了
  • @Daniel,我不这么认为:我没有从 T_RIDER 中选择当前值然后更新它,我只是更新了它。一旦 A 提交并释放锁,B 就会获取要更新的行,并在创建自己的之前看到 A 的更改。
  • 在通过连接视图进行多表插入和更新的变异表可能会失败,并且在处理并发的非单行插入/更新时可能会出现死锁问题。在单独的提交物化视图中查看 tmp_ponylist。
猜你喜欢
  • 2015-01-07
  • 1970-01-01
  • 1970-01-01
  • 2015-06-26
  • 1970-01-01
  • 2020-09-28
  • 2022-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多