您的代码中有一些错误:
我有下表:
CREATE TABLE review
(
review_id NUMBER(2) NOT NULL,
review_date DATE NOT NULL,
review_rating NUMBER(1) NOT NULL,
driver_no NUMBER(2) NOT NULL,
vehicle_id NUMBER(3) NOT NULL
);
CREATE TABLE testing
(
testing_id NUMBER(2) NOT NULL,
testing_start DATE NOT NULL,
testing_end DATE NOT NULL
driver_no NUMBER(2) NOT NULL,
vehicle_id NUMBER(3) NOT NULL
);
基本上,驾驶员在两个日期之间对车辆进行了测试。测试完成后,驾驶员会检查车辆。
我想创建一个触发器,以防止添加无效的评论。如果驾驶员在测试结束日期之前对车辆进行了审查,则该审查无效。如果驾驶员对未驾驶的车辆进行了审查,则该审查也无效。
例如,驾驶员1从2019年2月1日至2019年2月7日对车辆7进行了测试。如果为2019年2月5日添加了评论,我希望触发器能够防止插入。另外,如果增加了对车辆5的评价(当车辆7是被测试的车辆时),我希望触发器防止插入该信息。
这是我到目前为止所拥有的:
CREATE OR REPLACE TRIGGER review_check_validity
AFTER INSERT ON review
FOR EACH ROW
BEGIN
SELECT testing_start
FROM testing
WHERE driver_no = :new.driver_no;
SELECT vehicle_id
FROM testing
WHERE driver_no = :new.driver_no;
IF :new.review_date < testing_end THEN
raise_application_error(-20000, 'Review date cannot be before
testing end date');
END IF;
IF :new.vehicle_id != vehicle_id THEN
raise_application_error(-20000, 'Driver has never driven this
vehicle');
END IF;
END;
/
该触发器编译时没有任何错误-但是,当我尝试通过在REVIEW表中插入无效的行来对其进行测试时,会收到一条错误消息,说明
精确获取返回的行数超过了请求的行数
有人可以指出我需要对代码进行哪些更改才能获得所需的结果?
我确实认为您在SELECT访存中应该有一个INTO子句。我在查询中添加了一些注释,以尝试帮助您清除它。
CREATE OR REPLACE TRIGGER review_check_validity
AFTER INSERT ON review
FOR EACH ROW
-- Need to declare variables for usage in your SELECT fetches.
DECLARE
var_revdate number; -- or date, if applicable
var_revvehi number; -- or varchar(n), if applicable
BEGIN
-- In here you should have an INTO clause to assign your date parameter into
-- the predefined variable. As well, you need to ensure this fetch will provide
-- only one row.
SELECT testing_start INTO var_revdate -- why are you fetching "testing_start"?
FROM testing
WHERE driver_no = :new.driver_no;
-- Same case, it can only retrieve one row. If you need to do more than one row,
-- you may need to use a BULK & a LOOP.
SELECT vehicle_id INTO var_vehicid
FROM testing
WHERE driver_no = :new.driver_no;
IF :new.review_date < testing_end THEN
raise_application_error(-20000, 'Review date cannot be before testing end date');
END IF;
IF :new.vehicle_id != var_vehicid THEN
raise_application_error(-20000, 'Driver has never driven this vehicle');
END IF;
END;
/
希望有所帮助。
我将在这里重新排列逻辑:
在包含触发器代码的Oracle PL / SQL中,您不能仅单击SELECT
。您必须SELECT INTO
一个变量。然后,您可以在逻辑中使用该变量。
同样重要,当您SELECT INTO
一个变量时,查询只能返回一个结果。多行将触发您遇到的错误。
允许这样做,则CREATE OR REPLACE TRIGGER review_check_validity AFTER INSERT ON review FOR EACH ROW DECLARE testEnd DATE; vehicleTestCount NUMBER; BEGIN SELECT COUNT(*) INTO vehicleTestCount FROM testing WHERE vehicle_id = :new.vehicle_id; IF vehicleTestCount = 0 THEN raise_application_error(-20000, 'Driver has never driven this vehicle'); END IF; -- Assumes one test per driver per vehicle SELECT testing_end INTO testEnd FROM testing WHERE driver_no = :new.driver_no AND vehicle_id = :new.vehicle_id; IF :new.review_date < testEnd THEN raise_application_error(-20000, 'Review date cannot be before testing end date'); END IF; END; /
最后,您的表格结构允许同一位驾驶员对同一辆车进行多次测试。如果应该
review
表应通过testing
而不是testing_id
和driver_no
链接到vehicle_id
表。 您的代码中有一些错误:
在PL / SQL中,选择查询必须具有INTO
子句,将从SELECT查询中提取的数据存储到某些变量中。 -您的代码中缺少该部分
您正在测试表中仅带有driverno
的信息,这将为您提供该driverno
的所有记录(由该driveno
测试的所有车辆)。您需要同时包含vehicleid
和driverno
才能获取与当前评论相关的测试的详细信息。
因此您的触发代码可以按如下方式重写:
CREATE OR REPLACE TRIGGER REVIEW_CHECK_VALIDITY BEFORE INSERT ON REVIEW -- USING BEFORE INSERT TRIGGER TO AVOID ANY UNDOs FOR EACH ROW DECLARE LV_TEST_END_DATE DATE; LV_TEST_COUNT NUMBER; BEGIN -- FETCHING RELEVANT DATA USING SINGLE QUERY SELECT COUNT(1), MAX(TESTING_END) -- PLEASE HANDLE THE SCENARIO WHERE THE TESTING END DATE IS NULL INTO LV_TEST_COUNT, LV_TEST_END_DATE FROM TESTING WHERE VEHICLE_ID = :NEW.VEHICLE_ID AND DRIVER_NO = :NEW.DRIVER_NO; IF LV_TEST_COUNT = 0 THEN RAISE_APPLICATION_ERROR(-20000, 'Driver has never driven this vehicle'); ELSIF :NEW.REVIEW_DATE < LV_TEST_END_DATE THEN RAISE_APPLICATION_ERROR(-20000, 'Review date cannot be before testing end date'); END IF; END; /
干杯!
您的代码中有一些错误: