returning ... into ... 有没有并发风险

returning ... into ... 有没有并发风险

比如有一个单据表,如果状态为“未审核”,那么就允许删除;如果为“已审核”,那么就不允许删除;

所以对于删除这个功能,我们可以写成一下的存储过程:

create  procedure  Del_Order
(P_单号 varchar2)

is

v_状态   varchar2(20);
begin
  begin
    select  状态 into  v_状态  from 单据表 where 单号=p_单号;
  exception
    when_no_data_found then
    dbms_output.putline('单号'||p_单号||'不存在');
    return;
   end;

    if v_状态='未审核'
    then
       delete from  单据表   where 单号=p_单号;
      dbms_output.putline('单号'||p_单号||'删除成功');
   else
          dbms_output.putline('单号'||p_单号||'已审核,不能删除');
    end if;
end;


以上方法,可能很多人都用到; 但是我认为 很危险;

因为 这里是:先通过查询判断状态,再执行删除操作;再这两个操作之间,可能状态已经发生变化,所以可能导致并发异常;
所以,以上是一个危险的写法;

可以这样改进:

把第一句查询 :  select  状态 into  v_状态  from 单据表 where 单号=p_单号;
后面加一个 for update
这样可以锁住,就不会导致并发异常;




但是我觉得该方法不好;
有经验的开发人员,对于pl/sql最好做“进攻”式编程;
所以,我的一般风格是:


create  procedure  Del_Order
(P_单号 varchar2)

is

v_状态   varchar2(20);
begin
  
  delete from  单据表   where 单号=p_单号 and  状态='未审核';
  if sql%rowcount>0
  then
      dbms_output.putline('单号'||p_单号||'删除成功');
  else
       begin
        select  状态 into  v_状态  from 单据表 where 单号=p_单号;
      exception
     when_no_data_found then
         dbms_output.putline('单号'||p_单号||'不存在');
         return;
     end;
     dbms_output.putline('单号'||p_单号||'已审核,不能删除');
end if;
   
end;

该方法,我觉得有两个优点:
1.提高运行效率,如果大部分情况下,都是执行成功的,那么可以少做一步判断;
2.不会有并发风险;


下面是第3种方法,用returning :

create  procedure  Del_Order
(P_单号 varchar2)

is

v_状态   varchar2(20);
begin
    delete from  单据表   where 单号=p_单号
  returing 状态 into  v_状态;
  if sql%rowcount>0
  then
      if v_状态='未审核'
      then
        dbms_output.putline('单号'||p_单号||'删除成功');
      else
        rollback;
        dbms_output.putline('单号'||p_单号||'已审核,不能删除');
        return;
      end if;
  else
         dbms_output.putline('单号'||p_单号||'不存在');
         
    end if;
   
end;

如果从写代码角度看,第3种方法代码最少,如果没有问题,第 3种为首选,
不过与第2种方法比,我觉得有两个缺点:
  1.有可能有并发风险,  delete from  单据表   where 单号=p_单号   returing 状态 into  v_状态 这句话能保证
   返回的状态一定是删除后的状态,但想想也不可能,删除后怎么再去取状态,肯定是先取状态,再删除,那么就有个疑问,
   万一先取的状态是“为审核”,但是正在执行的时候就是“已审核”,这样就会把不允许删除的也删掉了,产生和第一种方法一样的并发异常,因为“delete from  单据表   where 单号=p_单号   returing 状态 into v_状态" 这句话同时做两件事情,所以不知道是否有并发异常的风险,也不容易测试;我猜测是没有并发异常的;但是对于 delete .. returing ..into...这种写法,毕竟是一句话做两件事情,谁知道它内部是如何执行的呢?我们是否可以把它理解成“select  ..from ..for update; delete from .. ”这两句话的合并呢?

   2.如果大部分情况都是删除成功的,那么第2种方法效率高,不需要再去执行查询状态的操作;但是第3种方法代码少,如果没有并发风险,也值得推荐。


所以我这个帖子其实有两个问题:

1. 对于删除单据这样一个简单操作,最好的写法风格是什么(结合 稳定,高效,代码少综合考虑)?
2. delete .. from .. returing ... 这一句话,可以理解成哪两句话的合并?有没有并发异常问题?

===========================================================
第二个问题:

可以把Oracle的SQL语句理解为"原子"操作,不用担心在语句执行内部会有别的操作打断

实际上语句执行的整个过程中, 看到的都是语句执行前的数据的快照, 不管别的进程执行过什么操作,它都是看不到的。 这个对于所有DML语句都是一样的,比如说select语句:
你执行一个耗时长的select, 在执行过程中别的进程修改了你将要查询的数据, 但是你的select语句是不会看到这个更改的。

有关这个问题, 建议你多看看oracle关于undo方面的文章。



第一个问题:
节省行数没有任何意义, 不断的有意删除数据然后再回滚大大的增加了数据库的负担, 第3种方法不提倡。
第1种方法带上for update 与第二种方法都没问题。 个人喜欢用第二种方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值