Oracle触发器:如何实现它们?

时间:2017-10-26 21:17:20

标签: oracle plsql foreign-keys database-trigger

完成学校作业,建立数据库。我的数据库模拟了摄影业务。大部分都很简单,但现在我被卡住了。所以在我的模型中,我有一个Photography_Availability实体(Ph_Av),每当摄影师被安排或不可用时,都会有一个相应的条目,其中包含摄影师不可用的日期和时间间隔(Available_Date,Start_Time_Available,Fin_Time_Available)。所有这些都是我的Oracle建模器中的DateTime数据类型。我想实现一个触发器,可以检查每当有人试图安排摄影师时,如果他们是否可用(在适当的日期和时间间隔内)。这是我的代码:

CREATE OR REPLACE TRIGGER SAFE_SCHEDULE
BEFORE INSERT OR UPDATE ON PH_AV 
FOR EACH ROW
DECLARE
    ID VARCHAR(20) := NULL;
    AV_DATE DATE := NULL;
    START_TIME DATE := NULL;
    FIN_TIME DATE := NULL;
BEGIN
    SELECT PHOTOGRAPHER_ID INTO ID FROM PH_AV WHERE PHOTOGRAPHER_ID = :NEW.PHOTOGRAPHER_ID ;

    SELECT AVAILABILITY_DATE INTO AV_DATE FROM PH_AV WHERE PHOTOGRAPHER_ID = :NEW.PHOTOGRAPHER_ID;

    SELECT START_TIME_AVAIL INTO START_TIME FROM PH_AV WHERE PHOTOGRAPHER_ID = :NEW.PHOTOGRAPHER_ID;

    SELECT FIN_TIME_AVAIL INTO FIN_TIME FROM PH_AV WHERE PHOTOGRAPHER_ID = :NEW.PHOTOGRAPHER_ID;

    IF ((:NEW.START_TIME_AVAIL >= START_TIME AND :NEW.START_TIME_AVAIL <= FIN_TIME)
    OR (:NEW.FIN_TIME_AVAIL >= START_TIME AND :NEW.FIN_TIME_AVAIL <= FIN_TIME)
    OR (:NEW.START_TIME_AVAIL <= START_TIME AND :NEW.FIN_TIME_AVAIL >= FIN_TIME))
        THEN
        DBMS_OUTPUT.PUT_LINE('Cannot be scheduled, photographer unavailable');
    ELSE
    DBMS_OUTPUT.PUT_LINE('PHOTOGRAPHER SUCCESSFULLY SCHEDULED');
    END IF;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            DBMS_OUTPUT.PUT_LINE('NO DATA FOUND');
        WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('ERROR-' || SQLERRM);
END;
/

我想要的基本逻辑是,比如摄影师已经安排在下午3点到5点之间,有人试图在下午2点到4点之间安排同一位摄影师,这显然会发生碰撞。我的if语句检查新插入的值是否与任何可能的调度值冲突。现在,在我运行之后,脚本输出并没有说明有任何错误。但我的消息日志声称相当多(其中大多数是&#34; null(内部错误)没有可投掷的堆栈元素&#34;)。

这是我第一次实现触发器,我想它可能是我声明的变量的Date数据类型,但我不知所措。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

如下所示的简单结构AVAIL以及防止条目重叠的要求,我会做类似以下的事情:

CREATE SEQUENCE avail_sq;

CREATE TABLE avail
(
   avail_id  INTEGER,
   person_id INTEGER,
   start_dtm DATE,
   end_dtm DATE
);

CREATE OR REPLACE TRIGGER test_avail_trg
FOR INSERT OR UPDATE ON avail
COMPOUND TRIGGER
   TYPE avail_tt IS TABLE OF avail%ROWTYPE;

   avail_t avail_tt := avail_tt();
   l_cnt   INTEGER;

   BEFORE EACH ROW IS
   BEGIN
      avail_t.EXTEND;
      avail_t(avail_t.COUNT).avail_id := :new.avail_id;
      avail_t(avail_t.COUNT).person_id := :new.person_id;
      avail_t(avail_t.COUNT).start_dtm := :new.start_dtm;
      avail_t(avail_t.COUNT).end_dtm := :new.end_dtm;
   END BEFORE EACH ROW;

   AFTER STATEMENT IS
   BEGIN
      FOR i IN avail_t.FIRST..avail_t.LAST LOOP
         SELECT COUNT(*)
           INTO l_cnt
           FROM avail a
          WHERE a.person_id = avail_t(i).person_id
            AND ((a.start_dtm <= avail_t(i).start_dtm AND a.end_dtm > avail_t(i).start_dtm) OR
                 (a.start_dtm > avail_t(i).start_dtm AND a.start_dtm < avail_t(i).end_dtm))
            AND a.avail_id != avail_t(i).avail_id; -- Can't overlap itself

         IF l_cnt > 0 THEN
            raise_application_error(-20000, 'Overlap detected');
         END IF;
      END LOOP;
   END AFTER STATEMENT;
END;
/

我没有从表中检索数据,而是进行了更多的存在检查。两个日期/时间过滤器检查在任何点重叠的条目。第一个检查现有条目,在新行开始之前或同时开始,在新行开始之后结束。第二个检查现有条目,这些条目在新行之后开始,也在新行结束之前开始。

因为您还允许对这些字段进行更新,所以您需要使用COMPOUND TRIGGER,它允许您收集每行所需的数据,但也可以在语句完成后处理它们。突变已经完成。