我正在尝试使用postgresql创建在两个表,课程和注册之间工作的函数/触发器。在注册表中插入新行时,触发器应更新课程表中的相应注册值,并将已满的开放统计信息设置为false。
我试图从注册表中选择相应的课程,并使用以下代码更新相应的表:
表格:
?
功能和触发器:
create table students (
id integer primary key,
name varchar(24)
);
create table courses (
num varchar(6) primary key,
open boolean not null,
enrolled integer default 0,
lim integer default 3
);
create table enrollment (
student integer references students(id),
course varchar(6) references courses(num)
);
假设将以下数据插入到“学生和课程”表中:
create or replace function func_1() returns trigger as
$$
declare
enrol integer;
limt integer;
begin
select enrolled into enrol from courses where num = (select course from enrollment where course = new.course);
select lim into limt from courses where num = (select course from enrollment where course = new.course);
if enrol<limt then
update courses set enrolled = enrol+1 where num = (select course from enrollment where course = new.course);
if enrol = limt-1 then
update courses set open = False where num = (select course from enrollment where course = new.course);
end if;
end if;
return new;
end;
$$ language plpgsql;
create trigger trigger_1
after insert
on enrollment
execute procedure func_1();
运行此命令时:
insert into students values(1, 'Alice');
insert into students values(2, 'Bob');
insert into courses values('CS1555', True);
insert into courses values('CS1501', True);
已将其插入到注册表中,但不会更新课程表中的相应行,注册的值未获得+1。
我也试图替换
insert into enrollment values(1, 'CS1501');
到
where num = (select course from enrollment where course = new.course)
在触发器中,但是如果我这样做了,它将更新课程表中的所有行(所有注册值都为+1),而不是相应的行。
请让我知道如何正确更新相应的值,谢谢!
答案 0 :(得分:1)
第一件事:学生不应多次报名参加同一门课程,而让学生报名不参加课程或空学生报名参加课程是没有意义的(除非您有充分的理由它)。
STOSB
+ UNIQUE
要定义。让我们使用NOT NULL
。
primary key
就数据库规范化而言,您的方法不好:create table enrollment (
student integer references students(id),,
course varchar(6) references courses(num),
PRIMARY KEY (student, course)
);
不(仅)依赖于密钥。
最好的解决方案是计算它:
enrolled
或者,您可以在1条语句中进行插入和更新表:
SELECT courses.*,
(SELECT COUNT(*) FROM enrollment WHERE enrollment.course = courses.num) AS enrolled
FROM courses
我可以详细说明一下您的触发器为何不起作用。
trigger的默认行为是在每个语句之后执行。您的代码要求指定WITH InsertStatement AS (
INSERT INTO enrollment VALUES(1, 'CS1501')
RETURNING course
)
UPDATE courses
SET enrolled = enrolled + AddedStudents
FROM (SELECT course, count(*) AS AddedStudents FROM InsertStatement GROUP BY course) Aux
WHERE courses.num = Aux.course
。
重新定义触发器,看看它是否可以解决您的问题。但是,我不保证此操作对并发插入有效(这就是为什么我的答案中有80%是关于避免使用triger的。)