如何在postgres的表上创建`而不是`触发器?

Yos*_*Yos 3 postgresql trigger insert functions

我想在 postgresql 中为一个表创建一个触发器。我的表包含有关事件的数据,包括房间号、开始时间和事件持续时间。在新插入表时,我想检查新事件的房间号在新事件发生时是否会很忙。如果是这样,那么我想引发异常,否则将新事件插入表中。我想将触发器声明为instead of触发器,insert但 postgres 不允许在表instead ofinsert操作触发器(我不想只为触发器创建视图)。

我该如何解决这个问题?理论上我可以创建一个after触发器,然后检查新插入的数据是否有效,如果无效则删除。但这在概念上似乎是错误的方法,实际上也可能是错误的(我不确定触发器是否是原子的,也许有人可能已经根据错误的数据做出了错误的决定)。

这是表定义:

create table room_schedule(
  start_date date,
  start_time time,
  room_no int,
  event_id int,
  duration interval,
  primary key(start_date, start_time, room_no, event_id)
);
Run Code Online (Sandbox Code Playgroud)

这是我的触发器和函数定义:

create or replace function inserttrigfunc() returns trigger as $$
declare count int;

begin

with end_time_table(eid, stime, etime) as (
        select event_id, start_time, (start_time + duration) as etime from room_schedule
        where room_no=new.room_no
    ),
        overlapping_time_table(eid, stime, etime) as(
            select * from end_time_table
            where (stime <= new.start_time and new.start_time <= etime) or
            (stime <= (new.start_time + new.duration) and (new.start_time + new.duration) <= etime)
        )
        select count(*) from overlapping_time_table into count;

        if count > 0
        then
            begin
                raise exception 'the room is already occupied at the time';
            end;
        else
            begin
                insert into room_schedule(start_date, start_time, room_no, event_id, duration)
                    values(new.start_date, new.start_time, new.room_no, new.event_id, new.duration);
            end;
end;
$$language plpgsql;





create trigger TRIG1 instead of insert
  on room_schedule
  for each row
  execute procedure inserttrigfunc();
Run Code Online (Sandbox Code Playgroud)

a_h*_*ame 5

您可以使用常规行级触发器实现您想要的。如果该触发器引发异常,insert则不会发生。如果您new从触发器返回,插入将继续。

从手册中引用

如果返回非空值,则操作继续使用该行值。返回与 NEW 的原始值不同的行值会改变将被插入或更新的行。因此,如果触发函数希望触发动作正常成功而不改变行值,则必须返回 NEW(或与其相等的值)

(强调我的)

基本上是这样的:

if count > 0 then
   raise exception 'the room is already occupied at the time';
else 
   return new;
end if;
Run Code Online (Sandbox Code Playgroud)

然后将触发器定义为:

create trigger TRIG1 before insert
  on room_schedule
  for each row
  execute procedure inserttrigfunc();
Run Code Online (Sandbox Code Playgroud)

我不确定触发器是否是原子的,也许有人已经根据错误的数据做出了错误的决定

是的,它们是触发触发器的事务的一部分。


但是你根本不需要触发器。

这很容易与实现排他条件使用两个日期的日期范围

create table room_schedule(
  start_date date,
  start_time time,
  room_no int,
  event_id int,
  duration interval,
  primary key(start_date, start_time, room_no, event_id),
  EXCLUDE USING gist (room_no with =, daterange(start_date, end_date, '[]') WITH &&)
);
Run Code Online (Sandbox Code Playgroud)

请注意,您需要上述扩展 btree gist因为默认情况下 GiST 索引不支持所需的相等性,因为约束应仅适用于相同的room_no