我们面临以增量方式访问Oracle行的问题,表模式是 -
myTable(UniqueNum,flag)
UniqueNum是一个数字不重复的字段,flag是默认值为F的字段。
我的编程步骤是:
获取UniqueNum,其中flag为F.
select min(UniquaNum)
from myTable
where flag='F';
执行操作。
一些操作(comport上的通信)
将标记设置为T。
Update myTable
set flag='T'
where flag='F'
and UniqueNum= 'UN'
我开发了一个执行这些步骤的exe(Delphi-7)。当exe同时在多台PC上运行时会出现问题。许多exe获取相同的UniqueNum(步骤1),但只有其中一个exe可以执行所有3个步骤。
假设PC1选择13
并执行步骤2,此时PC2执行步骤1,然后它也将获取13
。然后我的第3步将因PC2而失败。
我想知道的是,是否有某种机制可以避免这种类型的提取(行锁或表锁)?
有人能建议更好的解决方案来避免这种并发吗?如何在Delphi或C#中实现相同的功能?
答案 0 :(得分:1)
在第1步中,您需要执行“select .. for update nowait”。您必须检查查询是否成功并采取相应措施。
答案 1 :(得分:1)
由于你没有说失败意味着我只能假设任何一行是第二个会话必须等待或第二步失败处理,因为你在多个会话中的查询返回相同的最小数量。如果您使用的是11g以上,则可以使用for update skip locked
子句。但你是9i所以你必须效仿这一点。这是一种方式:
由于您所说的UniqueNum
(UniqueNum is a field where number does not repeat
是一个数字不重复的字段)包含唯一值,我们不必实际使用min()
函数,我们可以{{1} } order by
。
此处,函数UniqueNum
将替换您的next_min_number
查询。它的作用是它首先尝试锁定(具有最小select min() from
的行),如果行已经被锁定,它会尝试锁定下一个。一旦成功,它将返回uniquenum
的值。
UniqueNum
注意:具有create or replace function next_min_number
return number
is
l_res number;
row_locked exception;
pragma exception_init(row_locked, -54);
begin
for i in (select un
from t1
where flag = 'F' /*of course this value can be passed as parameter */
order by un )
loop
begin
-- trying to lock the row
select un
into l_res
from t1
where un = i.un
for update nowait;
/*row of interest is locked, exit the loop*/
exit;
exception
when row_locked then null;
when no_data_found then null;
end;
end loop;
return l_res;
end;
列中F
列的记录数
仅索引Flag
为Flag
的记录是个好主意。
注2:如果其中一列可以包含“NULL”,则必须相应地处理它。
测试用例:
设置:
F
会话#1:
create table t1(
un number,
flag varchar2(1)
);
insert into t1
select level
, 'F'
from dual
connect by level <= 3;
commit;
select *
from t1
UN FLAG
---------- ----
1 F
2 F
3 F
会话#2
set serveroutput on;
var min_num number;
-- your program
begin
:min_num := next_min_number;
dbms_output.put_line('Current min number: ' || to_char(:min_num));
dbms_output.put_line('Updating...');
end;
/
Current min number: 1
Updating...
PL/SQL procedure successfully completed.
我们可以看到同时运行的两个会话都有不同的数字。
然后每个会话都会发布更新。
会议#1set serveroutput on;
var min_num number;
-- your program
begin
:min_num := next_min_number;
dbms_output.put_line('Current min number: ' || to_char(:min_num));
dbms_output.put_line('Updating...');
end;
/
Current min number: 2
Updating...
PL/SQL procedure successfully completed
会议#2
update t1
set flag = 'T'
where flag = 'F'
and un = :min_num;
1 row updated.
commit;
结果:
update t1
set flag = 'T'
where flag = 'F'
and un = :min_num;
1 row updated.
commit;
答案 2 :(得分:0)
CREATE SEQUENCE customers_seq
START WITH 1000
INCREMENT BY 1
NOCACHE
NOCYCLE;
SELECT customers_seq.nextval from DUAL;
这是我知道超过1个客户端的稳定版本的唯一方法。每次您要求时,此选择将给出一个新的号码。
当然你可以把它封装成一个函数
function GiveNextID():integer;
或任何类似的事情。
据我记得,您可以将其作为列的默认值放入表定义中。