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

Oracle触发器:如何实现它们?,oracle,plsql,foreign-keys,database-trigger,Oracle,Plsql,Foreign Keys,Database Trigger,完成学校作业,建立数据库。我的数据库模拟了一个摄影行业。大部分都很简单,但现在我被卡住了。因此,在我的模型中,我有一个摄影可用性实体PHU Av,无论何时安排或不可用摄影师,都会有一个相应的条目,其中包含日期和时间间隔摄影师不可用日期、开始时间可用、结束时间可用。所有这些都是Oracle modeler中的日期时间数据类型。我想实现一个触发器,当有人试图安排摄影师时,它会检查他们是否在适当的日期和时间间隔内可用。这是我的密码: CREATE OR REPLACE TRIGGER SAFE_SCH

完成学校作业,建立数据库。我的数据库模拟了一个摄影行业。大部分都很简单,但现在我被卡住了。因此,在我的模型中,我有一个摄影可用性实体PHU Av,无论何时安排或不可用摄影师,都会有一个相应的条目,其中包含日期和时间间隔摄影师不可用日期、开始时间可用、结束时间可用。所有这些都是Oracle modeler中的日期时间数据类型。我想实现一个触发器,当有人试图安排摄影师时,它会检查他们是否在适当的日期和时间间隔内可用。这是我的密码:

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语句检查新插入的值是否与任何可能的计划值冲突。现在,在我运行这个脚本之后,脚本输出没有声明有任何错误。但我的消息日志声称,其中有相当一部分在内部错误No-Throwable Stack元素处为null

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

通过以下简单结构和防止条目重叠的要求,我将执行以下操作:

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;
/
我不是从表中检索数据,而是做更多的存在性检查。两个日期/时间过滤器检查在任何点重叠的条目。第一个检查在新行开始之前或同时开始并在新行开始之后结束的现有条目。第二个检查现有条目,这些条目在新行之后开始,也在新行结束之前开始


因为您还允许对这些字段进行更新,所以需要使用复合触发器,它允许您收集每一行所需的数据,但也可以在语句完成并完成变异后对其进行处理。

我编辑了您的标题:SQL不是拼写的续集,但更重要的是,触发器与SQL*Plus没有多大关系。问题是关于Oracle触发器。不相关,但是:从同一个表中读取四个不同的列不需要4次选择。在您的情况下,您甚至不需要任何选择。只需直接使用:new记录中的值-它们已经包含了您最想要的信息:with SELECT photogrator_ID。。。其中,摄影师_ID=:NEW.摄影师_IDI建议添加一个“descripe PH_AV”以查看列类型,也许错误输出日志将有助于显示所看到的内容。您应该查看数据模型。第一个日期数据类型总是包含日期和时间值,不需要将它们分开,即可用性\日期、开始\时间\可用性、最终\时间\可用性-这只会造成麻烦。当摄影师被安排好几次时,例如从下午3点到4点和4:30到5点,你如何处理这种情况。使用3到5个可用插槽很难处理此问题。不过,这对更新不起作用,不是吗?由于从要更新的表中选择将导致一个变异错误。you is corrent@kfinity,我在阅读原始问题时错过了更新要求。更新了一个复合触发器来解决这个问题。是的,我刚刚遇到了突变问题。基本上,我不能使用触发器来更改我正在读取的表。但我喜欢你的想法,尽管我不太理解第一学期与甲骨文互动的大部分内容。我想这是我必须处理的约束。如果我发现有人使用pragma自治事务来应对变异问题,我会发布一个解决方案