SQL 触发器不允许插入值

SQL trigger not not allowing to insert value

因此,我正在使用 Oracle 创建数据库,这就是我 运行 遇到的问题。我有这个 table:

CREATE TABLE T_SCHEDULE
(
    sched_id number(4) NOT NULL,
    master_id number(10) REFERENCES T_MASTER(master_id) ON DELETE CASCADE NOT NULL,
    client_id number(10) REFERENCES T_CLIENT(client_id) ON DELETE CASCADE NOT NULL,
    box_num number(5) REFERENCES T_BOX(box_num) ON DELETE CASCADE NOT NULL,
    car_num varchar2(10) REFERENCES T_CAR(car_num) ON DELETE CASCADE NOT NULL,
    price number(7) NOT NULL,
    job_start timestamp NOT NULL,
    job_stop timestamp NOT NULL,
    PRIMARY KEY(sched_id)
);

而这个触发器:

CREATE OR REPLACE TRIGGER HOURS_A_DAY
    BEFORE INSERT OR UPDATE ON T_SCHEDULE
    FOR EACH ROW
    DECLARE
    v_count number := 0;
    BEGIN
        SELECT COUNT(*)
        INTO v_count
        FROM (SELECT SUM(TO_NUMBER(TRUNC(TO_DATE(job_stop - job_start), 'MI'))) daily
            FROM T_SCHEDULE
            GROUP BY TRUNC(job_start, 'DD'), master_id)
        WHERE daily > 480;
        IF v_count <> 0 THEN
            ROLLBACK;
        END IF;
    END;

(table 引用了其他 table,但我认为它与问题无关)。 这是我要插入的值:

INSERT INTO T_SCHEDULE VALUES (4001, 1001, 2002, 3002, 'Р232ХВ', 20000, TIMESTAMP '2021-10-02 12:30:00.0', TIMESTAMP '2021-10-02 17:30:00.0');
INSERT INTO T_SCHEDULE VALUES (4002, 1002, 2003, 3003, 'А847КР', 8000, TIMESTAMP '2021-10-02 08:15:00.0', TIMESTAMP '2021-10-02 12:15:00.0');
INSERT INTO T_SCHEDULE VALUES (4003, 1003, 2004, 3005, 'С966ЕС', 5000, TIMESTAMP '2021-10-02 10:45:00.0', TIMESTAMP '2021-10-02 13:45:00.0');
INSERT INTO T_SCHEDULE VALUES (4004, 1004, 2005, 3001, 'Т138УВ', 10000, TIMESTAMP '2021-10-02 10:30:00.0', TIMESTAMP '2021-10-02 15:30:00.0');
INSERT INTO T_SCHEDULE VALUES (4005, 1005, 2006, 3006, 'Р364ВЕ', 15000, TIMESTAMP '2021-10-02 09:00:00.0', TIMESTAMP '2021-10-02 11:00:00.0');
INSERT INTO T_SCHEDULE VALUES (4006, 1001, 2007, 3005, 'О117УУ', 7000, TIMESTAMP '2021-10-03 14:10:00.0', TIMESTAMP '2021-10-02 17:10:00.0');
INSERT INTO T_SCHEDULE VALUES (4007, 1002, 2008, 3002, 'Н439АМ', 30000, TIMESTAMP '2021-10-03 10:40:00.0', TIMESTAMP '2021-10-03 15:40:00.0');
INSERT INTO T_SCHEDULE VALUES (4008, 1003, 2009, 3003, 'О896МТ', 4000, TIMESTAMP '2021-10-02 14:30:00.0', TIMESTAMP '2021-10-02 18:30:00.0');
INSERT INTO T_SCHEDULE VALUES (4009, 1004, 2010, 3004, 'Т694КС', 12000, TIMESTAMP '2021-10-03 09:50:00.0', TIMESTAMP '2021-10-03 17:50:00.0');
INSERT INTO T_SCHEDULE VALUES (4010, 1005, 2001, 3001, 'У601КК', 9000, TIMESTAMP '2021-10-02 16:00:00.0', TIMESTAMP '2021-10-02 20:00:00.0');

当我 运行 这样做时,插入了第一行,但是所有其他行都收到 ORA-01847(月中的日期必须在 1 和月的最后一天之间),ORA-06512(在“SYSTEM.HOURS_A_DAY”,第 4 行)和 ORA-04088(执行触发器 'SYSTEM.HOURS_A_DAY' 时出错)错误。我认为主要的是ORA-01847。所有这一切对我来说都很奇怪,因为我在同一个 table 中插入了相同的值,只是没有触发器,一切都很好。所以我猜我的触发器会影响它。我该如何解决?

您似乎在检查每一天 master_id 作业开始时间和作业结束时间之间的总时差是否超过 8 小时(在插入新行之前)。

可以简化为:

CREATE OR REPLACE TRIGGER HOURS_A_DAY
  BEFORE INSERT OR UPDATE ON T_SCHEDULE
  FOR EACH ROW
DECLARE
  v_hours number := 0;
BEGIN
  SELECT SUM(CAST(job_stop AS DATE) - CAST(job_start AS DATE)) * 24
  INTO   v_hours
  FROM   T_SCHEDULE
  WHERE  job_start >= TRUNC(:NEW.job_start)
  AND    job_start <  TRUNC(:NEW.job_start) + INTERVAL '1' DAY
  AND    master_id = :NEW.master_id;
      
  IF v_hours > 8 THEN
    RAISE_APPLICATION_ERROR(
      -20000,
      'Cannot schedule for more than 8 hours in a day.'
    );
  END IF;
END;
/

db<>fiddle here


更新 - 包括新行:

创建类型:

CREATE TYPE hours_detail IS OBJECT(
  master_id NUMBER(10),
  day       DATE,
  hours     NUMBER
);

CREATE TYPE hours_tbl IS TABLE OF hours_detail;

然后是复合触发器:

CREATE TRIGGER hours_a_day
FOR INSERT OR UPDATE ON t_schedule
COMPOUND TRIGGER
  hours        hours_tbl;
  v_overbooked PLS_INTEGER;
BEFORE STATEMENT
  IS
  BEGIN
    SELECT hours_detail(
             master_id,
             TRUNC(job_start),
             SUM(CAST(job_stop AS DATE) - CAST(job_start AS DATE)) * 24
           )
    BULK COLLECT INTO hours
    FROM   T_SCHEDULE
    GROUP BY master_id, TRUNC(job_start);
  END BEFORE STATEMENT;
BEFORE EACH ROW
  IS
  BEGIN
    hours.EXTEND();
    hours(hours.COUNT) := hours_detail(
      :NEW.master_id,
      TRUNC(:NEW.job_start),
      (CAST(:NEW.job_stop AS DATE) - CAST(:NEW.job_start AS DATE)) * 24
      - COALESCE(
          (CAST(:OLD.job_stop AS DATE) - CAST(:OLD.job_start AS DATE)) * 24,
          0
        )
    );
  END BEFORE EACH ROW;
AFTER STATEMENT
  IS
  BEGIN
    SELECT 1
    INTO   v_overbooked
    FROM   TABLE(hours)
    GROUP BY master_id, day
    HAVING SUM(hours) > 8
    FETCH FIRST ROW ONLY;

    RAISE_APPLICATION_ERROR(
      -20000,
      'Cannot schedule for more than 8 hours in a day.'
    );
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      NULL;
  END AFTER STATEMENT;
END;
/

当同时插入多个行时,单独总计不超过 8 小时但加在一起超过该限制时,这将引发错误。

db<>fiddle here

问题出在SUM(TO_NUMBER(TRUNC(TO_DATE(job_stop - job_start), 'MI')))job_stop - job_start,涉及两个 TIMESTAMP 的计算,returns 一个 INTERVAL 而不是 DATE 或 NUMBER。因此,您需要从结果间隔中提取天、小时、分钟和秒,将每个调整为以秒为单位的值。

您还有其他几个潜在问题:

  1. 您正在 T_SCHEDULE 上定义的触发器中选择 T_SCHEDULE。这可能会导致 ORA-04091“Table T_SCHEDULE 正在发生变化;触发器无法看到它”错误。解决方案是通过简单地删除 FOR EACH ROW 行,将其定义为语句触发器而不是行触发器。在这种情况下,这应该可行,但在许多情况下,这不是一个可行的解决方案,必须尝试其他解决方案。

  2. 您正在尝试在触发器中执行 ROLLBACK。这是不允许的 - 请改为引发异常。

把这些放在一起我们得到:

CREATE OR REPLACE TRIGGER HOURS_A_DAY
  BEFORE INSERT OR UPDATE ON T_SCHEDULE
DECLARE
  v_count number := 0;
BEGIN
  WITH cteInterval
    AS (SELECT JOB_START,
               MASTER_ID,
               job_stop - job_start AS JOB_INTERVAL
          FROM T_SCHEDULE)
  SELECT COUNT(*)
    INTO v_count
    FROM (SELECT SUM((EXTRACT(DAY    FROM JOB_INTERVAL) * 24 * 60 * 60) +
                     (EXTRACT(HOUR   FROM JOB_INTERVAL) * 60 * 60)      +
                     (EXTRACT(MINUTE FROM JOB_INTERVAL) * 60)           +
                     (EXTRACT(SECOND FROM JOB_INTERVAL))) / 60 AS daily
            FROM cteInterval
            GROUP BY TRUNC(job_start, 'DD'),
                     master_id)
    WHERE daily > 480;

  IF v_count <> 0 THEN
    RAISE_APPLICATION_ERROR(-20001, 'Difference greater than 8 hours found');
  END IF;
END HOURS_A_DAY;

db<>fiddle here