1

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

T_RIDER
  RIDER_ID
  TMP_PONYLIST
  ...

T_RIDER_PONY
  RIDER_ID
  PONY_ID

T_PONY
  PONY_ID
  PONY_NAME
  ...

T_RIDERT_PONY通过 建立n:m关系T_RIDER_PONYT_RIDERT_PONY有更多的列,但只有TMP_PONYLISTPONY_NAME这里相关。

TMP_PONYLIST是一个分号分隔的列表PONY_NAMES,想象一下"Twisty Tail;Candy Cane;Lucky Leaf"。无论T_RIDER_PONYT_PONY.

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

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

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

我正在使用 Oracle 数据库 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,那么这里有什么问题?

更新 文森特和托尼的答案不同,但都有帮助。我接受了托尼,但也请务必阅读文森特的回答。

4

2 回答 2

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;

有关11gR2 之前的字符串聚合的示例,请参见此 SO

于 2009-09-22T12:04:40.070 回答
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 之类的实用程序来处理它!有关详细信息,请参阅此 SO 答案

于 2009-09-22T11:34:32.113 回答