C# 基于Delphi7的Oracle并发控制

C# 基于Delphi7的Oracle并发控制,c#,oracle,delphi,oracle9i,C#,Oracle,Delphi,Oracle9i,我们面临以增量方式访问Oracle行的问题,表模式是- myTable(UniqueNum,flag) UniqueNum是一个数字不重复的字段,flag是默认值为F的字段 我的编程步骤是: 获取标志为F的唯一性 select min(UniquaNum) from myTable where flag='F'; 执行操作 关于comport通信的一些操作 将标志设置为T Update myTable set flag='T' where flag='F' an

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

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

我的编程步骤是:

获取标志为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获取相同的单一步骤1,但其中只有一个exe可以执行所有3个步骤

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

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


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

在步骤1中,您需要进行选择。。用于更新nowait。您必须检查查询是否成功,并采取相应措施。

Oracle的方法是使用序列

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

SELECT customers_seq.nextval from DUAL;
这是我所知道的超过1个客户端的稳定版本的唯一方法。每次您请求时,此选择将提供一个新号码

当然,您可以将其封装到函数中

function GiveNextID():integer;
或者其他类似的东西


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

因为您没有说明失败的意思,所以我只能假设其中一行是第二个会话必须等待的,或者步骤2处理失败,因为您在多个会话中的查询返回相同的最小值。如果您使用的是11g及以上版本,则可以利用FORUPDATE skip locked子句。但是你在9i上,所以你必须模仿这个。这里有一个方法:

由于UniqueNum正如您所说,UniqueNum是一个数字不重复的字段,是一个数字不重复的字段,包含唯一值,我们不必实际使用最小函数,我们可以按UniqueNum排序

这里的函数next_min_number将替换您的select min from查询。它所做的是尝试用min uniquenum锁定第一行,如果行已被锁定,则尝试锁定下一行。一旦成功,它将返回UniqueNum的值

注意:取决于您有多少条标志列中带有F的记录 只为标志为F的记录编制索引是个好主意

注2:如果其中任何一列可以包含“NULL”,则必须相应地处理它

测试用例:

设立:

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: 2                                                           
Updating...                                                                     
PL/SQL procedure successfully completed
正如我们所看到的,同时运行的两个会话得到了不同的数字

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

第1次会议

 update t1
    set flag = 'T'
  where flag = 'F'
    and un = :min_num;

1 row updated.

commit;
第2次会议

 update t1
    set flag = 'T'
  where flag = 'F'
    and un = :min_num;

1 row updated.

commit;
结果:

select *
  from t1;

        UN FLAG
---------- ----
         1 T   
         2 T   
         3 F   

3 rows selected.

这些步骤的代码示例在哪里:1、2、3?到底是什么问题?其他的锁上了吗?要避免这种类型的抓取,究竟什么类型的抓取?您使用的是什么版本的oracle?我使用的是oracle 9i,步骤还有-Step1从myTable中选择MininiQuanum,其中flag='F';步骤2-组件上的某些操作通信步骤3-更新myTable set flag='T',其中flag='F'和UniqueNum='UN'假设PC1选择13并执行步骤2,此时PC2执行步骤1,然后它也将获取13。然后,我的步骤3对于PC2将失败。对于PC2将失败。定义失败。如果在第3步后第一个会话没有提交或显示错误,我认为会发生锁定吗?您可以创建一个存储过程,该过程将返回唯一编号的值。这似乎是您的逻辑所在。请确保在完成获取后立即将标志设置为另一个状态,以避免其他请求返回相同的值。作为一般规则,您只希望选择实际要更改的记录,并使事务尽可能短。事务尽可能短在Oracle中,事务的长度应尽可能长—只要业务规则要求,而不是更短,也不是更长。Oracle不是SQL Server,您必须尽快提交才能处理锁定问题。^SQL Server也是。交易不应该比它必须的短或长。让我改变说法,这是一个糟糕的建议,因为它实际上不会阻止,但它仍然阻止其他PC取得任何进展。因为现在他们无法成功完成第一步。因此,该过程在步骤1-3中有效地序列化;问题又回到了客户那里。
 update t1
    set flag = 'T'
  where flag = 'F'
    and un = :min_num;

1 row updated.

commit;
select *
  from t1;

        UN FLAG
---------- ----
         1 T   
         2 T   
         3 F   

3 rows selected.