你需要實現的是一個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審覈隊列的工作方式。
此外,一次只分配一個任務。這樣你可以得到有效的工作分配。你想避免湯姆在盤子上有三項任務的情況,其中一項不會完成,而鮑勃無所事事。也就是說,除非你是鮑勃。
這取決於你如何定義「進入/離開系統」。簡單的方法:在每次請求期間,您都假設用戶「進入系統」。如果用戶已經分配任務,則一切正常。如果存在未完成的任務,則將一些任務分配給該用戶(即更新數據庫中的任務並記住用戶和時間戳),除非用戶分配了足夠的任務。如果用戶註銷或者有一段時間沒有被看到,您可以從任務表中刪除用戶及其時間戳。因此,您現在需要做的是:在每個請求期間更新任務及其分配。 –