我想使用另一列的聚合自动更新数据库列。 涉及三个表:
T_RIDER
RIDER_ID
TMP_PONYLIST
...
T_RIDER_PONY
RIDER_ID
PONY_ID
T_PONY
PONY_ID
PONY_NAME
...
T_RIDER
和T_PONY
通过T_RIDER_PONY
建立了 n:m 关系。
T_RIDER
和T_PONY
有更多列,但此处只有TMP_PONYLIST
和PONY_NAME
。
TMP_PONYLIST
是PONY_NAMES
的分号spararated列表,想象"Twisty Tail;Candy Cane;Lucky Leaf"
之类的内容。
无论T_RIDER_PONY
或T_PONY
发生什么情况,我都希望保持此字段为最新状态。
我的第一个想法是在T_RIDER_PONY
和T_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的答案不同但都很有帮助。 我接受了托尼,但一定要读文森的回答。
答案 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。