当两个表包含相同记录时抛出异常
Throwing an exception when two tables contain the same record
我正在尝试实现的 ER 模式调用两个 tables:
create table enrolled_in (
student_id number(6) not null,
course_crn number(6) not null,
constraint registers_for_pk primary key (student_id, course_crn),
constraint registers_for_s_fk foreign key (student_id) references student(id),
constraint registers_for_c_fk foreign key (course_crn) references course(crn)
);
create table registered_for (
student_id number(6) not null,
course_crn number(6) not null,
constraint preregisters_for_pk primary key (student_id, course_crn),
constraint preregisters_for_s_fk foreign key (student_id) references student(id),
constraint preregisters_for_c_fk foreign key (course_crn) references course(crn)
);
我必须创建某种完整性约束,以防止同一记录同时存在于两个 table 中。也就是说,学生可能无法注册他们注册的课程。
我的假设是我需要使用触发器,因为简单的约束不能调用 UDF(根据 CHECK 约束 documentation)。我还假设如果发现重复记录我应该抛出异常。这是我目前所拥有的:
create or replace trigger reg_duplicate_check
BEFORE insert on registers_for or enrolled_in
declare
duplicate_registration exception;
begin
select * into reg from registers_for;
FOR r in reg
LOOP
-- check for a duplicate record in enrolled_in?
END LOOP;
exception
when duplicate_registration then
raise_application_error(-20004,'Duplicate record (enrolled_in and registers_for)');
end;
我问两个问题:
我在右边吗track/is这是最好的方法吗?
如果是这样,那我该如何检查重复记录呢?如果不是,实现这种约束的最佳方法是什么?
我无法将它们合二为一table。
我对你问题的回答:
- 我认为你的方向不对。
- 您应该只使用一个 table,并附加一列,说明该行是否代表已注册或注册的课程学生。我将其命名为
what_is_it
(第 4 行)并检查约束(第 11 行),其中 E 代表“注册”,而 R 代表“注册”。如果需要,它也可以是外键。
像这样:
SQL> create table student_in_for
2 (student_id number(6),
3 course_crn number(6),
4 what_is_it varchar2(1),
5 --
6 constraint pk_stuinfor primary key (student_id, course_crn),
7 constraint fk_stuinfor_stu foreign key (student_id)
8 references student (id),
9 constraint fk_stuinfor_cou foreign key (course_crn)
10 references course (crn),
11 constraint ch_stuinfor_what check (what_is_it in ('E', 'R'))
12 );
Table created.
SQL>
这样做,它的主键会阻止具有相同 [student_id、course_crn] 组合的两行。
出于学习目的(因为你必须使用两个table),然后使用两个触发器 - 一个每个 table。他们很像:
create or replace trigger trg_biu_enr
before insert or update on enrolled_in
for each row
declare
l_cnt number;
begin
select count(*)
into l_cnt
from registered_for
where student_id = :new.student_id
and course_crn = :new.course_crn;
if l_cnt >= 1 then
raise_application_error(-20001, 'Student has already REGISTERED that course');
end if;
end;
/
create or replace trigger trg_biu_reg
before insert or update on registered_for
for each row
declare
l_cnt number;
begin
select count(*)
into l_cnt
from enrolled_in
where student_id = :new.student_id
and course_crn = :new.course_crn;
if l_cnt >= 1 then
raise_application_error(-20002, 'Student has already ENROLLED IN that course');
end if;
end;
/
I am not able to combine these into one table.
创建第三个 table 仅用于强制执行约束:
CREATE TABLE enrolment_registration (
student_id,
course_crn,
type CHAR(1),
CONSTRAINT enrolment_registration__sct__pk PRIMARY KEY (student_id, course_crn, type),
CONSTRAINT enrolment_registration__sc__u UNIQUE (student_id, course_crn),
CONSTRAINT enrolment_registration__t__chk CHECK (type IN ('E', 'R')),
CONSTRAINT enrolment_registration__s__fk
FOREIGN KEY (student_id) REFERENCES student(id),
CONSTRAINT enrolment_registration__c__fk
FOREIGN KEY (course_crn) REFERENCES course(crn)
);
CREATE TABLE enrolled_in (
student_id,
course_crn,
type CHAR(1) INVISIBLE GENERATED ALWAYS AS ('E'),
CONSTRAINT enrolled_in_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT enrolled_in_sc_fk FOREIGN KEY (student_id, course_crn, type)
REFERENCES enrolment_registration(student_id, course_crn, type)
);
CREATE TABLE registered_for (
student_id,
course_crn,
type CHAR(1) INVISIBLE GENERATED ALWAYS AS ('R'),
CONSTRAINT preregisters_for_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT preregisters_for_sc_fk FOREIGN KEY (student_id, course_crn, type)
REFERENCES enrolment_registration(student_id, course_crn, type)
);
然后从子 table 中创建一个触发器 insert/delete/update 行与父 table 中的匹配行并让 PRIMARY
/UNIQUE
父项 table 上的键强制执行约束。
或者,只需创建子 table,然后使用 MATERIALIZED VIEW
将它们 UNION
在一起。类似于:
CREATE TABLE enrolled_in (
student_id,
course_crn,
CONSTRAINT enrolled_in_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT enrolled_in_s_fk FOREIGN KEY (student_id) REFERENCES students(id),
CONSTRAINT enrolled_in_c_fk FOREIGN KEY (course_crn) REFERENCES courses(crn)
);
CREATE TABLE registered_for (
student_id,
course_crn,
CONSTRAINT preregisters_for_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT preregisters_for_sc_fk FOREIGN KEY (student_id) REFERENCES students(id),
CONSTRAINT preregisters_for_sc_fk FOREIGN KEY (course_crn) REFERENCES courses(crn)
);
CREATE MATERIALIZED VIEW enrolment_registration_mv
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS
SELECT student_id, course_crn, 'E' AS type FROM enrolled_in
UNION ALL
SELECT student_id, course_crn, 'R' AS type FROM registered_for;
ALTER VIEW enrolment_registration_mv
ADD CONSTRAINT enrolment_registration_mv__sc__pk
PRIMARY KEY (student_id, course_crn);
我正在尝试实现的 ER 模式调用两个 tables:
create table enrolled_in (
student_id number(6) not null,
course_crn number(6) not null,
constraint registers_for_pk primary key (student_id, course_crn),
constraint registers_for_s_fk foreign key (student_id) references student(id),
constraint registers_for_c_fk foreign key (course_crn) references course(crn)
);
create table registered_for (
student_id number(6) not null,
course_crn number(6) not null,
constraint preregisters_for_pk primary key (student_id, course_crn),
constraint preregisters_for_s_fk foreign key (student_id) references student(id),
constraint preregisters_for_c_fk foreign key (course_crn) references course(crn)
);
我必须创建某种完整性约束,以防止同一记录同时存在于两个 table 中。也就是说,学生可能无法注册他们注册的课程。
我的假设是我需要使用触发器,因为简单的约束不能调用 UDF(根据 CHECK 约束 documentation)。我还假设如果发现重复记录我应该抛出异常。这是我目前所拥有的:
create or replace trigger reg_duplicate_check
BEFORE insert on registers_for or enrolled_in
declare
duplicate_registration exception;
begin
select * into reg from registers_for;
FOR r in reg
LOOP
-- check for a duplicate record in enrolled_in?
END LOOP;
exception
when duplicate_registration then
raise_application_error(-20004,'Duplicate record (enrolled_in and registers_for)');
end;
我问两个问题:
我在右边吗track/is这是最好的方法吗?
如果是这样,那我该如何检查重复记录呢?如果不是,实现这种约束的最佳方法是什么?
我无法将它们合二为一table。
我对你问题的回答:
- 我认为你的方向不对。
- 您应该只使用一个 table,并附加一列,说明该行是否代表已注册或注册的课程学生。我将其命名为
what_is_it
(第 4 行)并检查约束(第 11 行),其中 E 代表“注册”,而 R 代表“注册”。如果需要,它也可以是外键。
像这样:
SQL> create table student_in_for
2 (student_id number(6),
3 course_crn number(6),
4 what_is_it varchar2(1),
5 --
6 constraint pk_stuinfor primary key (student_id, course_crn),
7 constraint fk_stuinfor_stu foreign key (student_id)
8 references student (id),
9 constraint fk_stuinfor_cou foreign key (course_crn)
10 references course (crn),
11 constraint ch_stuinfor_what check (what_is_it in ('E', 'R'))
12 );
Table created.
SQL>
这样做,它的主键会阻止具有相同 [student_id、course_crn] 组合的两行。
出于学习目的(因为你必须使用两个table),然后使用两个触发器 - 一个每个 table。他们很像:
create or replace trigger trg_biu_enr
before insert or update on enrolled_in
for each row
declare
l_cnt number;
begin
select count(*)
into l_cnt
from registered_for
where student_id = :new.student_id
and course_crn = :new.course_crn;
if l_cnt >= 1 then
raise_application_error(-20001, 'Student has already REGISTERED that course');
end if;
end;
/
create or replace trigger trg_biu_reg
before insert or update on registered_for
for each row
declare
l_cnt number;
begin
select count(*)
into l_cnt
from enrolled_in
where student_id = :new.student_id
and course_crn = :new.course_crn;
if l_cnt >= 1 then
raise_application_error(-20002, 'Student has already ENROLLED IN that course');
end if;
end;
/
I am not able to combine these into one table.
创建第三个 table 仅用于强制执行约束:
CREATE TABLE enrolment_registration (
student_id,
course_crn,
type CHAR(1),
CONSTRAINT enrolment_registration__sct__pk PRIMARY KEY (student_id, course_crn, type),
CONSTRAINT enrolment_registration__sc__u UNIQUE (student_id, course_crn),
CONSTRAINT enrolment_registration__t__chk CHECK (type IN ('E', 'R')),
CONSTRAINT enrolment_registration__s__fk
FOREIGN KEY (student_id) REFERENCES student(id),
CONSTRAINT enrolment_registration__c__fk
FOREIGN KEY (course_crn) REFERENCES course(crn)
);
CREATE TABLE enrolled_in (
student_id,
course_crn,
type CHAR(1) INVISIBLE GENERATED ALWAYS AS ('E'),
CONSTRAINT enrolled_in_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT enrolled_in_sc_fk FOREIGN KEY (student_id, course_crn, type)
REFERENCES enrolment_registration(student_id, course_crn, type)
);
CREATE TABLE registered_for (
student_id,
course_crn,
type CHAR(1) INVISIBLE GENERATED ALWAYS AS ('R'),
CONSTRAINT preregisters_for_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT preregisters_for_sc_fk FOREIGN KEY (student_id, course_crn, type)
REFERENCES enrolment_registration(student_id, course_crn, type)
);
然后从子 table 中创建一个触发器 insert/delete/update 行与父 table 中的匹配行并让 PRIMARY
/UNIQUE
父项 table 上的键强制执行约束。
或者,只需创建子 table,然后使用 MATERIALIZED VIEW
将它们 UNION
在一起。类似于:
CREATE TABLE enrolled_in (
student_id,
course_crn,
CONSTRAINT enrolled_in_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT enrolled_in_s_fk FOREIGN KEY (student_id) REFERENCES students(id),
CONSTRAINT enrolled_in_c_fk FOREIGN KEY (course_crn) REFERENCES courses(crn)
);
CREATE TABLE registered_for (
student_id,
course_crn,
CONSTRAINT preregisters_for_pk PRIMARY KEY (student_id, course_crn),
CONSTRAINT preregisters_for_sc_fk FOREIGN KEY (student_id) REFERENCES students(id),
CONSTRAINT preregisters_for_sc_fk FOREIGN KEY (course_crn) REFERENCES courses(crn)
);
CREATE MATERIALIZED VIEW enrolment_registration_mv
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS
SELECT student_id, course_crn, 'E' AS type FROM enrolled_in
UNION ALL
SELECT student_id, course_crn, 'R' AS type FROM registered_for;
ALTER VIEW enrolment_registration_mv
ADD CONSTRAINT enrolment_registration_mv__sc__pk
PRIMARY KEY (student_id, course_crn);