Oracle变异表错误

时间:2014-12-16 23:12:04

标签: oracle plsql triggers

我有一个

posts(id, likes)(likes = number of likes for that post)

post_likes(post_id, user_id)(the users who liked a post)

我有一个触发器(post_likes_after_insert)

所以当我添加新的时候会增加posts.likes和另一个减少(post_likes_before_delete)的触发器posts.likes。一切正常。

然后我为帖子添加了另一个触发器(before delete),以删除post_likes,但这会触发上面的触发器,从而减少posts.likes列。

所以它给了我这个错误:

  

帖子正在变异,触发器/功能可能无法看到它    post_likes_before_delete ,第4行;执行触发器时出错 post_likes_before_delete

这是一个带有代码的pastebin,底部是完整的错误。 http://pastebin.com/EQHTLZFp

感谢。

----------------------------------------------------------------------- 
--  USERS
-----------------------------------------------------------------------
CREATE TABLE users
(
        id NUMBER(10) PRIMARY KEY NOT NULL,
        email CHAR(100) UNIQUE NOT NULL,
        password CHAR(60) NOT NULL,
        first_name CHAR(30) NOT NULL,
        last_name CHAR(30) NOT NULL,
        birth_date DATE NULL,
        gender CHAR(10) NULL,
        joined TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
        CONSTRAINT gender_check CHECK (UPPER(gender) IN ('M', 'F'))
);

-- Auto increment for users.id
DROP SEQUENCE users_sequence;
CREATE SEQUENCE users_sequence START WITH 1 INCREMENT BY 1;
CREATE TRIGGER users_before_insert 
BEFORE INSERT
        ON users REFERENCING NEW AS NEW 
        FOR EACH ROW
BEGIN 
        SELECT users_sequence.nextval INTO :NEW.ID FROM dual; 
END;
/

-- 
-- POSTS
-- 
CREATE TABLE posts
(
        id NUMBER(10) PRIMARY KEY NOT NULL,
        user_id NUMBER(10) REFERENCES users(id) NOT NULL,
        created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
        content char(1000) NOT NULL,
        likes NUMBER(10) DEFAULT 0 NOT NULL,
        privacy CHAR(10) DEFAULT 'public' NOT NULL,
        CONSTRAINT fk_posts FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- Auto increment for posts.id
DROP SEQUENCE posts_sequence;
CREATE SEQUENCE posts_sequence START WITH 1 INCREMENT BY 1;
CREATE TRIGGER posts_before_insert 
BEFORE INSERT
        ON posts REFERENCING NEW AS NEW 
        FOR EACH ROW
BEGIN 
        SELECT posts_sequence.nextval INTO :NEW.ID FROM dual; 
END;
/

CREATE TRIGGER posts_before_delete 
BEFORE DELETE
        ON posts
        FOR EACH ROW
BEGIN
        DELETE FROM post_likes WHERE post_id = :old.id;
END;
/

-- 
-- POST_LIKES
-- 
CREATE TABLE post_likes
(
        user_id NUMBER(10) NOT NULL REFERENCES users(id),
        post_id NUMBER(10) NOT NULL REFERENCES posts(id),
        PRIMARY KEY (user_id, post_id),
        --CONSTRAINT fk_post_likes1 FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
        CONSTRAINT fk_post_likes2 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- Increments posts.likes  
CREATE TRIGGER post_likes_after_insert 
AFTER INSERT
        ON post_likes
        FOR EACH ROW

DECLARE
   n NUMBER(10);
BEGIN 
        SELECT likes INTO n FROM posts WHERE id = :new.post_id;
        UPDATE posts SET likes = n+1 WHERE id = :new.post_id;
END;
/

-- Decrements posts.likes
CREATE TRIGGER post_likes_before_delete
BEFORE DELETE
        ON post_likes
        FOR EACH ROW

DECLARE
   n NUMBER(10);
BEGIN 
        SELECT likes INTO n FROM posts WHERE id = :old.post_id;
        UPDATE posts SET likes = n-1 WHERE id = :old.post_id;
END;
/

-- Full error, EUSEBIU is the username that I'm logged in
-- ERROR at line 1:
-- ORA-04091: table EUSEBIU.POSTS is mutating, trigger/function may not see it
-- ORA-06512: at "EUSEBIU.POST_LIKES_BEFORE_DELETE", line 4
-- ORA-04088: error during execution of trigger 'EUSEBIU.POST_LIKES_BEFORE_DELETE'
-- ORA-06512: at "EUSEBIU.POSTS_BEFORE_DELETE", line 2
-- ORA-04088: error during execution of trigger 'EUSEBIU.POSTS_BEFORE_DELETE'

1 个答案:

答案 0 :(得分:0)

看看如果删除帖子会发生什么。您的触发器尝试删除post_likes。这就是为什么你的另一个触发器试图按你的意愿减少posts.likes。所以,你试图做出不必要的行动并看到变异,因为在帖子上删除会导致帖子更新。

你应该做的就是打破这个链条。你完全删除帖子时不应该尝试改变喜欢。例如,您可以使用类似

的内容
create or replace trigger MY_BEFORE_DELETE_LIKE_TRIGGER ...
begin
  -- Let's do nothing if deleting master too
  if dbms_utility.format_call_stack like '%MY_BEFORE_DELETE_POSTS_TRIGGER%' then
    return;
  end if;
  ...

更正确的方法是将flag用于打包变量或类似的东西。