让我们说如果我有以下内容:
create table jobs
(
staff_id number,
job_name varchar2(1000) not null,
start_date date not null,
end_date date not null
);
这只是一张列出员工各种工作的表格。
现在员工一次只能完成一项工作,所以我使用以下PL / SQL语句插入作业。如果找到一个冲突的工作,没有添加任何工作(我应该在这里报告错误,但是对于这个简化的例子,我忽略了这一点):
create or replace procedure add_job
(
p_staff_id number,
p_job_name varchar2,
p_start_date date,
p_end_date date
)
as
begin
insert into jobs
(
select p_staff_id, p_job_name, p_start_date, p_end_date from dual
where not exists
(
select 1 from jobs
where staff_id = p_staff_id
and (end_date > p_start_date)
and (start_date < p_end_date)
)
);
end;
问题是,如果我在两个不同的会话中添加两个不同的作业,然后提交,我可以加倍预订。即以下内容:
-- Session 1:
add_job(1, 'Help Alice', to_date('2011/08/21 11:00:00', 'YYYY/MM/DD HH24/MI/SS'), to_date('2011/08/21 13:00:00', 'YYYY/MM/DD HH24/MI/SS'));
-- Session 2:
add_job(1, 'Help Bob', to_date('2011/08/21 12:00:00', 'YYYY/MM/DD HH24/MI/SS'), to_date('2011/08/21 14:00:00', 'YYYY/MM/DD HH24/MI/SS'));
-- Session 1:
commit;
-- Session 2:
commit;
以上,staff_id
1将在12:00至13:00之间重复预订。
似乎添加到我的程序的开头:
lock table jobs in exclusive mode;
做了这个伎俩,我觉得这太宽了。有没有办法强迫oracle做一些更细粒度的事情。如果可能的话,我宁愿不与dbms_lock
混在一起。这个page暗示select ... for update
可以解决问题,但它没有提供详细信息。
有没有办法在没有完整的桌锁或dbms_lock
锁定的情况下停止双重预订? (如果有所不同,我正在使用Oracle 10g。)
答案 0 :(得分:2)
你显然需要锁定以防止双重预订。由于您需要锁定特定的工作人员,我的建议是在插入语句之前在工作人员中锁定一行:
select id from staff where id = p_staff_id for update;
这样,锁只影响一个工作人员(假设你有行级别锁)。
答案 1 :(得分:0)
您可以使用数据库trigger来强制执行员工职位的单一性。
如果找到单个工作人员的重叠工作,那么它将不允许插入,因此您无法获得重叠的工作。
在您的问题指定的情况下,无论哪个作业被提交,第二个都会因触发器检查插入而失败。
BTW,锁定ENTIRE表不是一个好的解决方案。此外,尝试使用SELECT
FOR UPDATE
也不会阻止两次插入。
答案 2 :(得分:0)
您可以考虑使用具有检查约束的物化视图的Onw方式。请参阅this old blog post of mine哪里有例子“1)规则:员工不能重叠项目分配”与您的非常相似。
警告:维护实体化视图会影响性能,因此如果没有正确的性能测试,请不要实现。
答案 3 :(得分:0)
只要工作分配在相当大的时间段内,1小时或30分钟(1/24/60 * 30)
你可以在staff_id&amp;上创建一个带有primay键的新表scheduled_time_slot。时间 (或日期和插槽编号)
如果为每个使用的时段插入一行: (以下示例在开始和结束时间之间获取每30分钟的时间段并存储它)
INSERT INTO booked_time_slot
SELECT p_staff_id,TO_DATE('2011/08/21 11:00:00', 'YYYY/MM/DD HH24/MI/SS') +
((ROWNUM-1)*(1/24/60*30))
FROM DUAL
CONNECT BY TO_DATE('2011/08/21 11:00:00', 'YYYY/MM/DD HH24/MI/SS') +
((ROWNUM-1)*(1/24/60*30)) <
TO_DATE('2011/08/21 12:00:00', 'YYYY/MM/DD HH24/MI/SS')
主键将停止任何重复。
答案 4 :(得分:-2)
如果staff_id应该是唯一的,您应该按顺序生成它。
当你进行插入时,你就是这样做的:
insert into jobs
(
select seq_staff_id.nextval, p_job_name, p_start_date, p_end_date from dual
)