Eri*_*art 3 oracle triggers delete-row ora-04091
我现在正在为 Oracle 中的 DELETE 触发器进行长期斗争,该触发器在删除一行后,从剩余行中选择一个新的 MAX 值并将其写入另一个表。在偶然发现烦人的 ORA-04091 变异表错误(在 FOR EACH ROW 中无法读取表)之后,我切换到了 Oracle 的复合触发器。
如何最好地存储已删除的行(每行多个值,因为只有当删除的分数可能是高分,而不是较低分数时,进一步检查才会更新)?我担心,如果多个触发事件交叉触发,并且例如为“DeletedMatches”运行高分更新(该更新实际上并未被删除,而是由 Before 触发事件注册),则全局临时表可能会陷入混乱。
我可以创建一个表,a) 仅存在于该触发器中 b) 可以像普通数据库表或临时表一样在 SQL 中使用吗?
每当删除匹配项时,以下(伪)代码将更新 CurrentHighScores 表(旧的高分消失并被剩余的最高分取代)。
CREATE TABLE GameScores (
MatchId number not null --primary key
Player varchar(255) not null,
Game varchar(255) not null, -- PacMan, Pong, whatever...
Score number not null );
-- High score for each game:
CREATE TABLE CurrentHighScores (
HiScId number not null --primary key
Player varchar(255) not null,
Game varchar(255) not null,
HighScore number not null );
create or replace TRIGGER UpdHiScoreOnMatchDelete
FOR DELETE ON GameScores
COMPOUND TRIGGER
TYPE matchtable IS TABLE OF GameScores%ROWTYPE INDEX BY SIMPLE_INTEGER;
DeletedMatches matchtable;
MatchIndex SIMPLE_INTEGER := 0;
BEFORE EACH ROW IS -- collect deleted match scores
BEGIN
MatchIndex:= MatchIndex+ 1;
DeletedMatches(MatchIndex).Game := :old.Game;
DeletedMatches(MatchIndex).Score := :old.Score;
-- don't want to set every column value, want to
-- do like: INSERT :old INTO DeletedMatches;
-- don't want the Index either!
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
UPDATE CurrentHighScores hsc
SET hsc.HighScore=(
select max(gsc.Score) from GameScores gsc
where hsc.Game=gsc.Game)
where hsc.Game IN (
select del.Game from DeletedMatches del where hsc.HighScore = del.Score)
-- won't work, how can I check within the SQL if a row
-- for this game has been deleted, or anyhow integrate
-- DeletedMatches into the SQL, without a cursor?
-- Optional further cond. in subselect, to update only
-- if deleted score equals highscore:
and exists(
select 1 from GameScores where Game=hsc.Game);
-- ignore games without remaining match scores.
-- Delete/set zero code for games without existing scores omitted here.
END AFTER STATEMENT;
Run Code Online (Sandbox Code Playgroud)
“烦人的”变异表错误几乎总是表明设计不佳,通常是非规范化的数据模型。这似乎适用于本案。如果您需要维护聚合值、计数、最大值等,为什么不使用 Oracle 的内置功能呢?Oracle 为我们提供了专门用于处理摘要的 MATERIALIZED VIEW 对象。 了解更多。
在您的情况下,将 CurrentHighScores 替换为物化视图。
CREATE MATERIALIZED VIEW CurrentHighScores
BUILD IMMEDIATE
REFRESH FAST
as select
(
Player ,
Game ,
max(score) as HighScore
from GameScores
group by player, game ;
Run Code Online (Sandbox Code Playgroud)
您还需要在 GameScore 上构建一个物化视图日志。