如何使用触发器功能填充表格?

时间:2014-12-08 22:01:31

标签: postgresql

我有一个功能:

CREATE OR REPLACE FUNCTION delete_student()
RETURNS TRIGGER AS 
$BODY$
BEGIN
IF (TG_OP = 'DELETE')
THEN
INSERT INTO cancel(eno, excode,sno,cdate,cuser)
VALUES ((SELECT entry.eno FROM entry
     JOIN student ON (entry.sno = student.sno)
     WHERE entry.sno = OLD.sno),(SELECT entry.excode FROM entry
     JOIN student ON (entry.sno = student.sno)
     WHERE entry.sno = OLD.sno),
     OLD.sno,current_timestamp,current_user);
END IF;
RETURN OLD;
END; $BODY$ LANGUAGE plpgsql;

我也有触发器:

CREATE TRIGGER delete_student
BEFORE DELETE
on student
FOR EACH ROW
EXECUTE PROCEDURE delete_student();

这个想法是当我从学生关系中删除学生时,条目关系中的条目也会删除,我的取消关系会更新。

这就是我对学生的关系:

INSERT INTO 
student(sno, sname, semail) VALUES (1, 'a. adedeji', 'ayooladedeji@live.com');

这就是我加入我的入门关系:

INSERT INTO
entry(excode, sno, egrade) VALUES (1, 1, 98.56);

当我执行命令

DELETE FROM student WHERE sno = 1;

它会删除学生以及相应的条目并且查询返回时没有错误但是当我在取消表上运行select时,表显示为空?

2 个答案:

答案 0 :(得分:0)

您没有显示如何删除相应的entry。如果在student记录之前删除该条目,则会导致该问题,因为触发器中的INSERT将失败,因为SELECT语句将不提供要插入的任何值。是否通过entry上的CASCADING删除删除了相应的student

此外,您的触发器可以更简单:

CREATE OR REPLACE FUNCTION delete_student() RETURNS trigger AS $BODY$
BEGIN
    INSERT INTO cancel(eno, excode, sno, cdate, cuser)
    VALUES (SELECT eno, excode, sno, current_timestamp, current_user
            FROM entry
            WHERE sno = OLD.sno);
    RETURN OLD;
END; $BODY$ LANGUAGE plpgsql;

首先,该功能仅在DELETE触发器上触发,因此您无需测试TG_OP。其次,在INSERT语句中,您永远不会访问student关系中的任何数据,因此不需要JOIN该关系; sno确实来自学生关系,但是来自OLD隐式参数。

答案 1 :(得分:0)

您没有发布您的数据库架构,并且您的问题不是很清楚,但看起来级联删除正在干扰某处。具体做法是:

  1. 在删除student之前,您需要在引用它的cancel中插入内容。
  2. Postgres继续删除student中的行。
  3. Postgres继续尊重所有适用的级联删除。
  4. Postgres会删除entry和... cancel中的行(包括您刚刚插入的行)。
  5. 一些评论:

    首先,作为经验法则,before触发器除了行本身之外不应该对任何东西产生副作用。在before delete触发器中插入行是一个很大的问题:除了引入相关的潜在问题,例如Postgres在完成查询时报告错误的FOUND值或不正确的行数,请考虑单独的before触发器的情况通过返回NULL完全取消删除。因此,您的触发器函数应该在after触发器上运行 - 只有在那时您才能确定该行确实已被删除。

    其次,你不需要这些低效,冗余和丑陋的子选择语句。请改用insert ... select ...种插页:

    INSERT INTO cancel(eno, excode,sno,cdate,cuser)
    SELECT entry.eno entry.excode, OLD.sno, current_timestamp, current_user
    FROM entry
    WHERE entry.sno = OLD.sno;
    

    第三,你的触发器可能应该在条目表上运行,如下所示:

    INSERT INTO cancel(eno, excode,sno,cdate,cuser)
    SELECT OLD.eno OLD.excode, OLD.sno, current_timestamp, current_user;
    

    最后,您的架构中可能存在一些问题。如果entry中的每一行student都有一个唯一的行,并且您需要entry中的信息才能使您的触发器工作以填充cancel,表示应该合并两个表(studententry)。无论是否合并它们,您可能还需要删除(或手动管理)适用的某些级联删除,以便按照运行所需的顺序强制执行业务逻辑。