我想在尝试表中插入行时将行插入另一个名为excercise_scores的表中。根据评分方法(可以是最新得分,所有尝试的平均值或所有尝试的最高得分),有多次尝试进行练习,我必须在excercise_scores表中插入得分。
因此,当将分数插入excercise_score时,我还必须检查练习的评分方法是什么,根据方法从尝试表中获取分数,然后最终插入到excercise_scores表中。
为此,我创建了一个SQL触发器,如下所示
CREATE OR REPLACE TRIGGER scores_trigger AFTER
INSERT
ON attempts FOR EACH row DECLARE P1 VARCHAR2(50);
SCORES FLOAT;
v_exists VARCHAR2(1) := 'F';
BEGIN
BEGIN
SELECT
'T'
INTO
v_exists
FROM
excercise_scores
WHERE
user_id = :NEW.USER_ID
AND EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
EXCEPTION
WHEN no_data_found THEN
NULL;
END;
SELECT
SELECTION_METHOD
INTO
P1
FROM
EXCERCISES
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
IF (P1 = 'Latest') THEN
SCORES := :NEW.TOTAL_POINTS;
ELSE
IF (P1 = 'best') THEN
SELECT
MAX(TOTAL_POINTS)
INTO
SCORES
FROM
ATTEMPTS
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID
AND USER_ID = :NEW.USER_ID;
ELSE
SELECT
AVG(TOTAL_POINTS)
INTO
SCORES
FROM
ATTEMPTS
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID
AND USER_ID = :NEW.USER_ID;
END IF;
END IF;
IF v_exists = 'T' THEN
UPDATE
EXCERCISE_SCORES
SET
TOTAL_POINTS = SCORES
WHERE
user_id = :NEW.USER_ID
AND EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
ELSE
INSERT
INTO
EXCERCISE_SCORES VALUES
(
:NEW.EXCERCISE_ID,
:NEW.COURSE_ID,
:NEW.COURSE_ID,
SCORES
) ;
END IF;
END;
但是当我尝试执行此触发器时,它会给我以下错误:
Error starting at line 47 in command:
insert into attempts values(21,0,'vshesha',3,'CSC540',TO_DATE('20141014', 'YYYYMMDD'),12,6)
Error report:
SQL Error: ORA-04091: table VSHESHA.ATTEMPTS is mutating, trigger/function may not see it
ORA-06512: at "VSHESHA.SCORES_TRIGGER", line 18
ORA-04088: error during execution of trigger 'VSHESHA.SCORES_TRIGGER'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
我无法弄清楚为什么它会在插入后运行触发器时发出此错误。一般来说,在插入触发器之后不应出现变异表问题。
任何人都可以帮助我。我很久以来都很沮丧。
答案 0 :(得分:2)
错误消息非常自我解释,导致它的SQL语句是:
SELECT
AVG(TOTAL_POINTS)
INTO
SCORES
FROM
ATTEMPTS
WHERE
EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID
AND USER_ID = :NEW.USER_ID;
您正在从ATTEMPTS
中进行选择,这是当前正在更改的表格。没有简单的解决方案 - 最明智的方法是使用将您的业务逻辑从触发器移到应用程序代码中(参见AskTom about mutating table errors)。
顺便说一句:你的声明"一般来说,在插入触发器后不应该出现变异表问题。"是完全错的。有关详细说明,请参阅database journal article(简而言之:您将始终遇到多行插入的变异表错误,并且几乎每种情况都会遇到错误单行插入)。
答案 1 :(得分:0)
在Oralce中,您可以使用COMPOUND TRIGGER。在你的情况下,它将类似于这一个。我没有将你的所有操作复制到这个例子中,但你应该得到原则。
然而,正如其他回复已经提到的那样。首选方法是将所有这些逻辑移到PL / SQL过程中。
CREATE OR REPLACE TRIGGER scores_trigger
FOR INSERT ON attempts
COMPOUND TRIGGER
TYPE RowTableType IS TABLE OF ROWID;
RowTable RowTableType;
aRow attempts%ROWTYPE;
SCORES FLOAT;
v_exists VARCHAR2(1) := 'F';
BEFORE STATEMENT IS
BEGIN
RowTable := RowTableType();
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
RowTable.EXTEND;
RowTable(RowTable.LAST) := :NEW.ROWID;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN RowTable.FIRST..RowTable.LAST LOOP
SELECT *
INTO aRow
FROM attempts
WHERE ROWID = RowTable(i);
SELECT SELECTION_METHOD
INTO P1
FROM EXCERCISES
WHERE EXCERCISE_ID = aRow.EXCERCISE_ID
AND COURSE_ID = aRow.COURSE_ID;
IF P1 = 'Latest' THEN
SCORES := :NEW.TOTAL_POINTS;
ELSE
IF (P1 = 'best') THEN
SELECT MAX(TOTAL_POINTS)
INTO SCORES
FROM ATTEMPTS
WHERE EXCERCISE_ID = aRow.EXCERCISE_ID
AND COURSE_ID = aRow.COURSE_ID
AND USER_ID = aRow.USER_ID;
ELSE
SELECT AVG(TOTAL_POINTS)
INTO SCORES
FROM ATTEMPTS
WHERE EXCERCISE_ID = aRow.EXCERCISE_ID
AND COURSE_ID = aRow.COURSE_ID
AND USER_ID = aRow.USER_ID;
END IF;
END IF;
END LOOP;
END AFTER STATEMENT;
END scores_trigger;
PL / SQL过程的一个例子是:
CREATE OR REPLACE PROCEDURE insert_score(V_EXCERCISE IN SCORES.EXCERCISE_ID%TYPE, V_COURSE IN SCORES.COURSE_ID%TYPE, V_USER IN USER_ID%TYPE) IS
BEGIN
INSERT INTO insert_score (EXCERCISE_ID, COURSE_ID, USER_ID) VALUES (V_EXCERCISE, V_COURSE, V_USER);
BEGIN
SELECT 'T'
INTO v_exists
FROM excercise_scores
WHERE user_id = :NEW.USER_ID
AND EXCERCISE_ID = :NEW.EXCERCISE_ID
AND COURSE_ID = :NEW.COURSE_ID;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN;
END;
... other stuff
END insert_score;
对于插入,您必须调用此过程而不是直接执行插入。