pl/sql。编写复合触发器时,after every 语句不会在 before 语句子句之前定义变量

问题描述 投票:0回答:1

我在模式 MARSEL 上有一个表 Uspev,其中包含学生(学生 ID)、predmet(科目 ID)、ocenka(分数)和数据(考试日期)列。我需要编写一个复合触发器,禁止学生每天参加两次以上考试(不关心哪一场考试,只关心参加考试的事实)。教授提供了一个触发器的例子,但它导致了“mutating-table”错误。 至于我的代码,在 before 语句之前声明了变量,即记录数组和计数器。虽然计数器没有以红色突出显示,但 SELECT COUNT(*) 中使用的数组却以红色突出显示。我按照手册'https://docs.oracle.com/en/database/oracle//oracle-database/21/lnpls/database-pl-sql-language-reference.pdf'编写的,但它没有似乎有效。这是我的代码:

CREATE OR REPLACE TRIGGER MARSEL.STUD_USPEV
    FOR UPDATE OF DATA ON MARSEL.USPEV
    COMPOUND TRIGGER
    TYPE r_student_exam_date_type IS RECORD (  //a record type to hold a student ID
        stud        MARSEL.USPEV.STUDENT%TYPE, //and their exam date
        exam_date   MARSEL.USPEV.DATA%TYPE
    ); 
    TYPE t_student_exam_date_type IS TABLE OF r_student_exam_date_type //array type containing the records
        INDEX BY PLS_INTEGER;
    t_student_exam_date     t_student_exam_date_type; //the array declaration
    counter number;        //the counter will count how many exams a student have sat during a day being inserted
    
    BEFORE STATEMENT IS 
        BEGIN
            SELECT r_student_exam_date_type  // inserting all rows into the array
            BULK COLLECT INTO t_student_exam_date 
            FROM MARSEL.USPEV;
    END BEFORE STATEMENT;
    
    AFTER EACH ROW IS
        BEGIN
            counter := 0;
            SELECT COUNT(*) INTO counter  //counting existing dates with the same student ID as the one being inserted
            FROM t_student_exam_date  //HIGHLIGHTED AS UNDEFINED
            WHERE stud = :NEW.student AND exam_date = :NEW.data;
            IF (counter > 1) 
                THEN Raise_Application_Error(-20000, 'a student cannot sit more than two exams a day');
            END IF;
    END AFTER EACH ROW;
END STUD_USPEV;
oracle plsql triggers
1个回答
0
投票

不能在 SQL 查询中使用本地声明的集合类型;即使您执行了

TABLE(t_student_exam_data)
并删除了
INDEX BY
子句,您也会收到 ORA-22905: 无法访问非嵌套表项中的行。集合类型需要在架构级别创建或在包规范中声明才能在查询中使用。

您可以改为循环遍历集合,并为每个匹配记录增加计数器:

    AFTER EACH ROW IS
        BEGIN
            counter := 0;
            FOR i IN 1..t_student_exam_date.count
            LOOP
                IF t_student_exam_date(i).stud = :NEW.student
                    AND t_student_exam_date(i).exam_date = :NEW.data
                THEN
                    counter := counter + 1;
                END IF;
            END LOOP;

            IF counter > 1
            THEN
                Raise_Application_Error(-20000, 'a student cannot sit more than two exams a day');
            END IF;
    END AFTER EACH ROW;

你的批量收集也是错误的,但可能是在发布时更改错误;并且您添加的评论是非法的。您需要将触发器设置为

FOR INSERT OR UPDATE
 更改后,它现在可以工作了。

小提琴

您可能想查看边缘情况,例如将行更新为同一日期...

© www.soinside.com 2019 - 2024. All rights reserved.