你需要实现的是一个FIFO堆栈或简单队列。在Oracle中,最好的东西是SELECT ... FOR UPDATE
和SKIP LOCKED
子句,除非你想实现一个带有AQ的实际队列。 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>
这种方法的主要缺点是它要求用户在工作时保持有状态的会话e任务。它不是这种情况,那么你需要一个API,其中get_next_task()
是一个离散事务,并忘记锁定。
顺便说一下,它可能会更好,让用户下载,而不是通过登录触发器为它们分配一个任务(或任何你在心里有“汤姆进入系统,并获得任务1,2,3”) 。拉取任务是SO审核队列的工作方式。
此外,一次只分配一个任务。这样你可以得到有效的工作分配。你想避免汤姆在盘子上有三项任务的情况,其中一项不会完成,而鲍勃无所事事。也就是说,除非你是鲍勃。
这取决于你如何定义“进入/离开系统”。简单的方法:在每次请求期间,您都假设用户“进入系统”。如果用户已经分配任务,则一切正常。如果存在未完成的任务,则将一些任务分配给该用户(即更新数据库中的任务并记住用户和时间戳),除非用户分配了足够的任务。如果用户注销或者有一段时间没有被看到,您可以从任务表中删除用户及其时间戳。因此,您现在需要做的是:在每个请求期间更新任务及其分配。 –