您需要实现的是 FIFO 堆栈或简单队列。在 Oracle 中,对于这样的事情,最好的事情(除非你想用 AQ 实现一个实际的队列)是SELECT ... FOR UPDATE
使用SKIP LOCKED
子句。 SKIP LOCKED
允许我们轻松地操作多个用户的堆栈。
这是一个简单的界面:
create or replace package task_mgmt is
function get_next_task return tasks.id%type;
procedure complete_task (p_id in tasks.id%type);
procedure release_task (p_id in tasks.id%type);
end task_mgmt;
/
这是一个简单的实现:
create or replace package body task_mgmt is
function get_next_task return tasks.id%type
is
return_value tasks.id%type;
cursor c_tsk is
select id
from tasks
where status = 'open'
order by date_created, id
for update skip locked;
begin
open c_tsk;
fetch c_tsk into return_value;
update tasks
set status = 'progress'
, assigned = user
where current of c_tsk;
close c_tsk;
return return_value;
end get_next_task;
procedure complete_task (p_id in tasks.id%type)
is
begin
update tasks
set status = 'complete'
, date_completed = sysdate
where id = p_id;
commit;
end complete_task;
procedure release_task (p_id in tasks.id%type)
is
begin
rollback;
end ;
end task_mgmt;
/
当用户弹出堆栈时更新状态会创建一个锁。由于该SKIP LOCKED
子句,下一个用户将看不到该任务。这比删除和重新插入记录要干净得多。
这里有一些数据:
create table tasks (
id number not null
, descr varchar2(30) not null
, date_created date default sysdate not null
, status varchar2(10) default 'open' not null
, assigned varchar2(30)
, date_completed date
, constraint task_pk primary key (id)
)
/
insert into tasks (id, descr, date_created) values (1000, 'Do something', date '2015-05-28')
/
insert into tasks (id, descr, date_created) values (1010, 'Look busy', date '2015-05-28')
/
insert into tasks (id, descr, date_created) values (1020, 'Get coffee', date '2015-06-12')
/
让我们流行吧!这是第一节:
SQL> var tsk1 number;
SQL> exec :tsk1 := task_mgmt.get_next_task ;
PL/SQL procedure successfully completed.
SQL> print :tsk1
TSK1
----------
1000
SQL>
同时在第二场:
SQL> var tsk2 number;
SQL> exec :tsk2 := task_mgmt.get_next_task ;
PL/SQL procedure successfully completed.
SQL> print :tsk2
TSK2
----------
1010
SQL>
回到第一节:
SQL> exec task_mgmt.complete_task (:tsk1);
PL/SQL procedure successfully completed.
SQL> exec :tsk1 := task_mgmt.get_next_task ;
PL/SQL procedure successfully completed.
SQL> print :tsk1
TSK
----------
1020
SQL>
这种方法的主要缺点是它要求用户在处理任务时维护有状态的会话。情况并非如此,那么您需要一个get_next_task()
作为离散事务的 API,而忘记锁定。
顺便说一句,让用户获取任务可能比通过登录触发器分配任务更好(或者您想到的“Tom 进入系统并获取任务 1、2、3。”)。拉取任务是 SO Review 队列的工作方式。
此外,一次只分配一项任务。这样你就可以得到有效的工作分配。您想避免 Tom 有三项任务,其中一项他不会完成,而 Bob 无事可做的情况。也就是说,除非你是鲍勃。