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。因此,您需要从结果间隔中提取天、小时、分钟和秒,将每个调整为以秒为单位的值。
您还有其他几个潜在问题:
您正在 T_SCHEDULE 上定义的触发器中选择 T_SCHEDULE。这可能会导致 ORA-04091“Table T_SCHEDULE 正在发生变化;触发器无法看到它”错误。解决方案是通过简单地删除 FOR EACH ROW
行,将其定义为语句触发器而不是行触发器。在这种情况下,这应该可行,但在许多情况下,这不是一个可行的解决方案,必须尝试其他解决方案。
您正在尝试在触发器中执行 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;
因此,我正在使用 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。因此,您需要从结果间隔中提取天、小时、分钟和秒,将每个调整为以秒为单位的值。
您还有其他几个潜在问题:
您正在 T_SCHEDULE 上定义的触发器中选择 T_SCHEDULE。这可能会导致 ORA-04091“Table T_SCHEDULE 正在发生变化;触发器无法看到它”错误。解决方案是通过简单地删除
FOR EACH ROW
行,将其定义为语句触发器而不是行触发器。在这种情况下,这应该可行,但在许多情况下,这不是一个可行的解决方案,必须尝试其他解决方案。您正在尝试在触发器中执行 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;