Oracle阻止重复插入

Dan*_*ses 4 sql oracle race-condition

考虑一个有多个课程注册请求的系统.我们需要一种方法来阻止系统中的重复注册.我创建了一个触发器,如下所示,但是当我同时从不同的连接获得两个请求(相隔ms)时,它们都被插入.我究竟做错了什么

create trigger enrollment_duplicates
before insert
on enrollment
for each row
begin
    select count(*) 
      into cnt 
      from enrollment 
     where user = :new.user 
       and course = :new.course 
       and status = 'Enrolled';
    if cnt > 0 then
        raise_application_error(-20001, 'User already enrolled in course');
    end if;
end;
Run Code Online (Sandbox Code Playgroud)

编辑:

如果我们使用户/课程成为一个独特的约束,这很容易,但事实并非如此.他们可以根据状态重新注册.

Jus*_*ave 6

您需要一个唯一的索引.如果您说只有Enrolled一行但有许多行具有其他状态,则可以创建基于函数的索引

CREATE UNIQUE INDEX idx_stop_multiple_enrolls
    ON enrollment( (case when status = 'Enrolled' 
                         then user 
                         else null 
                      end),
                   (case when status = 'Enrolled' 
                         then course 
                         else null 
                      end) );
Run Code Online (Sandbox Code Playgroud)

这需要的是,Oracle不包含在索引值时,所有列的优势NULL,因此指数只有条目行,其中的statusEnrolled.

注意,这USER是一个保留字(有一个内置函数USER)所以我假设你的实际列被命名为不同的东西.

  • @DMoses - 触发器肯定不会阻止读提交隔离级别中不同会话中的重复插入.这就是在触发器中实现这种事情如此困难的原因之一.你可以做一些像`SELECT FOR UPDATE`这样的`student`表中的行,这样两个会话就不能在同一个时间点修改同一个学生的数据.这将序列化访问,但可能会产生许多其他问题. (2认同)