Thomas Zhang的杂货铺
18 06, 2008
编写健壮的PL/SQL代码(三)
作者 tomszrp 12:27 | Permalink 静态链接网址 | Comments 最新回复 (0) | Trackback 引用 (0) | 解决方案

继续忽悠。。。

我们大家都和送快递的打过交道,我们都知道快递员每天会按照客户和业务的分布,事先需要规划好行程。当快递员带着客户的东西来到一栋分为A、B、C、D四座的20多层高的写字楼下,他并不会急着去送某一个客户的东西,而是根据这座大楼里客户和货品的分布情况,先规划一下自己的路线,然后以最短的路线,最快的时间完成任务。而不会是先送到A座1楼101房间客户的单子,然后返回公司再去拿2楼的货,然后。。。,这样下去我想那个快递公司也快关门了。

 

大家都到ATM上取过钱吧?公司楼下周边工行、农行、招行、建行、及其他一些商业银行自助ATM机构,但分布在马路的不同方向和位置,有的离的近,有的离的远,有的支持银联有的不支持,你手上持有一张招行卡,工行离你最近,但你发现,这里经常需要排队,人较多,每次可能要等10-20分钟,农行离的稍微远一些,但一般人少,可以节省一点时间,但同样每个月跨行交易超过3笔每笔交易需要收2元的手续费,招行离你最远,需要步行10分钟,穿过一个十字路口,安全系数降低了,但通过招行取钱的话,不需要花手续费.

 

在这样的背景下,你几乎每个月都需要取好几次钱,每次的数目也不一样,而你又是一个精打细算、工作很忙的人,那么你不是就要好好的安排一下你的取钱计划呢?

 

在实际的PL/SQL代码编写过程中,和其他语言一样,如同上面的生活场景,根据实际的业务逻辑,我们很少写出那些单一的、线形控制的业务逻辑,现实情况往往是我们需要根据一些既定的或可计算的条件判断方式做出不同的处理.

 

在PLSQL语言中,Oracle也提供了相应的解决方案,让我们的代码和逻辑看上去透明和易于理解,那就是IF、ELSIF或CASE

 

看一下下面这段代码:

    create or replace procedure get_cash_cost(p_bankcode in varchar2) is
    begin
       if p_bankcode='NH' then
          dbms_output.put_line('Total Cost:200');
       end if;
       if p_bankcode='GH' then
          dbms_output.put_line('Total Cost:300');
       end if;
       if p_bankcode='ZH' then
          dbms_output.put_line('Total Cost:100');
       end if;
       if p_bankcode='JH' then
          dbms_output.put_line('Total Cost:400');
       end if;
       if p_bankcode='QT' then
          dbms_output.put_line('Total Cost:500');
       end if;
    end;
    /

根据所选的银行,get_cash_cost自动计算出到那个银行半业务的成本,可以清楚的看到,我这里list出来的条件都是相互独立的,是互相排斥的,彼此之间没有关系,但我在每次决定走哪条路线前,我需要计算每一个IF条件,这样是不是有点浪费?是不是增加了资源上的开销?

针对这种情况,改成下面的方式是不是效果要好一些呢?

    create or replace procedure get_cash_cast(p_bankcode in varchar2) is
    begin
       if p_bankcode='NH' then
          dbms_output.put_line('Total Cost:200');
       elsif p_bankcode='GH' then
             dbms_output.put_line('Total Cost:300');
          elsif p_bankcode='ZH' then
                dbms_output.put_line('Total Cost:100');
            elsif p_bankcode='JH' then
                  dbms_output.put_line('Total Cost:400');
               elsif p_bankcode='QT' then
                     dbms_output.put_line('Total Cost:500');
                  else
                     dbms_output.put_line('Unkown');
       end if;
    end;
    /

因为所有的条件都是独立的,那么现在只要有一个条件被计算为真,则所有其它条件计算都将被忽略,是不是节省了一些开销呢?很多时候资源就是被这些无谓的计算浪费掉的。很明显下面的两个调用

exec get_cash_cost('NH');

exec get_cash_cost('Unkown')

所需要的时间是不一样的,这个在通过Oracle 10g的time model statistics,可以得到一个验证。

对于上面的IF ELSIF子句,你是不是觉得看起来很费劲呢?很多时候,开发人员都有类似的通病,写一段代码,就一定要写的拐弯摸角,就是让你看不明白,显得好象自己很"牛"的样子,和你玩做迷藏。给后期的维护和代码升级带来了很大的麻烦。

 

所以我经常建议我的那些朋友在coding的时候,要尽量使自己的代码透明、结构清晰,易于理解。否则你不仅是让你的"接班人"头疼,大部分情况下你将自食苦果。

对于上面采用IF ELSIF子句的写法,从9i开始以后,我们可以使用更清晰的case(我在另外一篇文章中已经详细的介绍了case语句的使用方法,大家也可以参考)语句来代替,让你的代码上去很美。

    create or replace procedure get_cash_cost(p_bankcode in varchar2) is
    begin
       case p_bankcode
         when 'NH' then
           dbms_output.put_line('Total Cost:200');
         when 'GH' then
           dbms_output.put_line('Total Cost:300');
         when 'ZH' then
           dbms_output.put_line('Total Cost:100');
         when 'NH' then
           dbms_output.put_line('Total Cost:400');
         when 'QT' then
           dbms_output.put_line('Total Cost:500');
         else
           dbms_output.put_line('Unkown');
       end case;
    end;
    /

在真正的业务系统逻辑条件控制中,条件一般要复杂,有的时候甚至需要很多个条件通过一些的与|或|非来做一些复杂的判断。个人的习惯是尽量不要把这些负责的逻辑放在一层去判断,可以根据条件的优先级和过滤范围,分为多层去处理,对程序的可读性更有好处。

看下面的一段代码,理解起来是不是很痛苦:

    declare
      A boolean := TRUE;
      B boolean := FALSE;
      C boolean := FALSE;  
    begin
      if A and not (B or C) then
           dbms_output.put_line('A and not (B or C)');
      elsif A and (B or C) then
            dbms_output.put_line('A and (B or C)');
         elsif not A and not (B or C) then
                dbms_output.put_line('not A and not (B or C)');
             elsif not A and (B or C) then
                   dbms_output.put_line('not A and (B or C)');
      end if;
    end;
    /

我将上面的复杂的逻辑判断分为如下的2层,理解起来是不是要好多了:

    declare
      A boolean := TRUE;
      B boolean := TRUE;
      C boolean := FALSE;  
    begin
      if A then
         if (B or C) then
             dbms_output.put_line('A and (B or C)');
         else
             dbms_output.put_line('A and not (B or C)');
         end if;
      else
         if (B or C) then
             dbms_output.put_line('not A and (B or C)');
         else
             dbms_output.put_line('not A and not (B or C)');
         end if;
      end if;
    end;
    /  

这一小节就到这里了,健壮的PL/SQL语句不仅仅要设计合理,同时也要结构清晰、通俗易懂。往往这些小的细节方面,可以帮助我们正确的代码.


Comments
博客日历
« 八月 2008 »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
搜索
最新发表
文章分类
文章归档
网站链接
新闻聚合