Oracle 复合触发器 - 如何存储和 "use"删除的行?索引表?

标签 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;

最佳答案

“恼人的”变异表错误几乎总是表明设计不佳,通常是非规范化数据模型。这似乎适用于这种情况。在您需要维护聚合值、计数、最大值等时,为什么不使用 Oracle 的内置功能呢? Oracle 专门为我们提供了 MATERIALIZED VIEW 对象来处理摘要。 Find out more .

在您的情况下,将 CurrentHighScores 替换为物化 View 。

CREATE MATERIALIZED VIEW CurrentHighScores 
BUILD IMMEDIATE
REFRESH FAST
as select 
( 
    Player , 
    Game    , 
    max(score) as HighScore  
from GameScores 
group by player, game ; 

您还需要在 GameScores 上构建一个 MATERIALIZED VIEW LOG。

关于Oracle 复合触发器 - 如何存储和 "use"删除的行?索引表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11259254/

相关文章:

SQL 代替触发器

sql - 在“代替删除”触发器中删除记录

android - ListView 项目的删除选项总是删除第一个项目,无论单击哪个项目

oracle - Oracle 数据库的 NHibernate <timestamp> 映射导致 StaleStateException

sql - Oracle 虚拟列引用另一个表

Oracle 没有 bool 类型的 JavaScript 解决方法?

javascript - "Cannot find function replace"尝试替换部分字符串时出错

JQuery Mobile 触发器 ('create' ) 命令不起作用

java - 从 Oracle 中删除 1.9 亿条记录

sql - 在 Oracle SQL (11g) 中选择所有带有 "Now minus 30 minutes"的行