想测试一下存储过程处理过程中,另一个进程也调用了当前存储过程时,数据一致性的问题,现做了以下测试:
创建表A100(可理解成库存总量表) C1  varchar2(10) --产品编码
                                  C2  number        --产品库存总量      A200(可理解成备品出入库记录表。暂时不考虑主键问题)
                                  C1  varchar2(10)  --产品编码
                                  C2  number         --出入库量创建了两个存储过程SP_TEST1、SP_TEST2。两个存储过程大多数内容一样,只是SP_TEST1在Commit前多一条dbms_lock.sleep(5); 以模拟上一个处理尚未完成时,另一个处理又被调用。(存储过程见下面)调用方法:执行SP_TEST1,紧接着执行SP_TEST2(在5秒钟之内),发现执行结果与预想的出入很大
1)第一次调用存储过程时,A100表会生成两条“产品编码”一样的记录,“产品库存总量”不同,分别是两个存储过程调用是赋的值
2)第二次(包括之后)调用时,尽管先执行的SP_TEST1,但A100表合计总量仍会漏掉SP_TEST1执行时赋予的参数量请问:1)我这种实验方法本身有没有问题(即是否可以利用dbms_lock.sleep(5);模拟存储过程执行时间很长);
      2)若我的方法没有问题,为什么并发执行时SP_TEST1的参数没有被统计并更新到表A100?
      3)若我的存储过程有问题,如何编写才能避免存储过程并发调用时,库存量被错误计算(统计)?--存储过程SP_TEST001
CREATE OR REPLACE PROCEDURE sp_test001
(vName in varchar2,vNumb in number)
is
begin
  declare
       vA1Rcount number;
begin
  --判断A200表中是否有c1=vName的记录 如果有:update  没有:insert
  insert into A200(c1,c2) values (vName,vNumb);
  --判断A100表中是否有c1=vName的记录,如果有:update 没有:Insert
  select count(1) into vA1Rcount from A100 where c1=vName;
  if(vA1Rcount > 0) then
    update A100 set c2=(select sum(nvl(c2,0)) from A200 where A200.c1 = A100.c1) where A100.c1=vName;
  else
    insert into A100(c1,c2,c3) values (vName,vNumb,0);
  end if;
dbms_lock.sleep(5);
  commit;
  EXCEPTION WHEN OTHERS THEN
    DECLARE
        Err_msg varchar2(512):=sqlerrm;
    BEGIN
        Dbms_output.put_line('ORA Error message '||err_msg);
        Rollback;
    end;
  end;
end;
--存储过程SP_TEST002
CREATE OR REPLACE PROCEDURE sp_test002
(vName in varchar2,vNumb in number)
is
begin
  declare
       vA1Rcount number;
begin
  --判断A200表中是否有c1=vName的记录 如果有:update  没有:insert
  insert into A200(c1,c2) values (vName,vNumb);
  --判断A100表中是否有c1=vName的记录,如果有:update 没有:Insert
  select count(1) into vA1Rcount from A100 where c1=vName;
  if(vA1Rcount > 0) then
    update A100 set c2=(select sum(nvl(c2,0)) from A200 where A200.c1 = A100.c1) where A100.c1=vName;
  else
    insert into A100(c1,c2,c3) values (vName,vNumb,0);
  end if;
  commit;
  EXCEPTION WHEN OTHERS THEN
    DECLARE
        Err_msg varchar2(512):=sqlerrm;
    BEGIN
        Dbms_output.put_line('ORA Error message '||err_msg);
        Rollback;
    end;
--表A100
create table A100
(
  c1 VARCHAR2(10),
  c2 NUMBER)--表A200
create table A200
(
  c1 VARCHAR2(10),
  c2 NUMBER) 

解决方案 »

  1.   

    對於兩個不同的session來說,第一個會話沒有提交,第二個會話讀出的還是原來的記錄.執行時給當前記錄加行鎖
      

  2.   

    当两个不同的session对同一张表的同条记录进行update的时候,第一个session获得更新权后会对该行记录加一个行锁,这时如果其他的session对这条记录进行更新的时候,只有第一个session进行commit以后其他的session才能够对该条记录进行更新,在第一个session没有commit之前其他session查询出的数据信息是更新前的值。从你的存储过程中可以发现最后更新后的信息只会是第二个存储过程中的信息,对一个存储过程的更新会被第二个存储过程重新更新掉。
      

  3.   

    多谢两位的回复。那我怎么才能得到我期望的正确值?
    即:无论是否有两个session并发处理,合计“累积出库量”都是“正确的”??
      

  4.   

    搞定了~~就像第一个哥们儿说的,行锁
    1)在A100新增一个字段C1 NUMBER
    2)在SP_TEST1 SP_TES2开始所有处理语句前,新增一行
     select c3 into vRstatus from a100 where c1=vName for update;
    还是先行锁定这样,并发处理时,第二个执行的sp会等第一个sp执行完毕再继续多谢二位