并发控制Oracle与Delphi7

时间:2016-10-27 11:10:12

标签: c# oracle delphi oracle9i

我们面临以增量方式访问Oracle行的问题,表模式是 -

myTable(UniqueNum,flag)

UniqueNum是一个数字不重复的字段,flag是默认值为F的字段。

我的编程步骤是:

  1. 获取UniqueNum,其中flag为F.

    select min(UniquaNum) 
      from myTable 
     where flag='F';
    
  2. 执行操作。

  3. 一些操作(comport上的通信)

    1. 将标记设置为T。

      Update myTable 
         set flag='T' 
       where flag='F' 
         and UniqueNum= 'UN'
      
    2. 我开发了一个执行这些步骤的exe(Delphi-7)。当exe同时在多台PC上运行时会出现问题。许多exe获取相同的UniqueNum(步骤1),但只有其中一个exe可以执行所有3个步骤。

      假设PC1选择13并执行步骤2,此时PC2执行步骤1,然后它也将获取13。然后我的第3步将因PC2而失败。

      我想知道的是,是否有某种机制可以避免这种类型的提取(行锁或表锁)?

      有人能建议更好的解决方案来避免这种并发吗?如何在Delphi或C#中实现相同的功能?

3 个答案:

答案 0 :(得分:1)

在第1步中,您需要执行“select .. for update nowait”。您必须检查查询是否成功并采取相应措施。

答案 1 :(得分:1)

由于你没有说失败意味着我只能假设任何一行是第二个会话必须等待或第二步失败处理,因为你在多个会话中的查询返回相同的最小数量。如果您使用的是11g以上,则可以使用for update skip locked子句。但你是9i所以你必须效仿这一点。这是一种方式:

由于您所说的UniqueNumUniqueNum 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列的记录数       仅索引FlagFlag的记录是个好主意。

注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.

我们可以看到同时运行的两个会话都有不同的数字。

然后每个会话都会发布更新。

会议#1

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: 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)

Oracle方式是使用序列。

CREATE SEQUENCE customers_seq
 START WITH     1000
 INCREMENT BY   1
 NOCACHE
 NOCYCLE;

SELECT customers_seq.nextval from DUAL;

这是我知道超过1个客户端的稳定版本的唯一方法。每次您要求时,此选择将​​给出一个新的号码。

当然你可以把它封装成一个函数

function GiveNextID():integer;

或任何类似的事情。

据我记得,您可以将其作为列的默认值放入表定义中。