寻找更加清晰的方式来编码这个RPG LE

时间:2013-09-04 13:48:39

标签: ibm-midrange rpgle

---这里我们找到了我们要在其他文件中清除的客户编号。首先,我们正在阅读客户主数据,然后在订单历史记录或发票历史记录中查看客户编号是否存在。如果不是,那么我们希望从Customer master以及其他2个文件中清除此客户。

但是在第二个文件中,如果客户编号在营销列中有“A”或“C”,而且是在2007年之后,我们不希望从任何文件中清除这个。

所以我创建了代码,在将客户记录写入保存/保留文件并删除之前,它会返回一个标志,是的,可以删除。

C                   IF        PUGFIL = 'Y' AND        
C                             ACENT# <> ACENT#_OLD    
c                   EXSR      CHKCUS_SR               
c     ACFLAG        IFEQ      'N'                     
C                   WRITE     TRCMASRR                
c*                  delete    arcmasrr                

c     CHKCUS_SR     BEGSR      
c                   eval      ACFLAG = ' '                        
C     ORHKEY        SETLL     dRCST1                              
C     ORHKEY        READE     dRCST1                              
 * If the order entity is found, write the rec into VRCSTKBI file 
C                   DOW       NOT %EOF(dRCST1)                    
c                   if        BICOTC <> 'A' AND BICOTC <> 'C'     
C                   WRITE     VRCSTKRR                            
c                   EVAL      ACFLAG = 'N'                        
c                   endif                                         
c                   if        bicotc = 'A'                        
c                   if        BISTPD <  20070101                  
C                   WRITE     VRCSTKRR                            
c                   EVAL      ACFLAG = 'N'                        
c                   endif                                         
c                   endif                                         
c                   if        bicotc = 'C'                        
c                   if        BISTPD <  20070101                  
C                   WRITE     VRCSTKRR                            
c                   EVAL      ACFLAG = 'N'         
c                   endif                          
c                   endif                          
c     acflag        ifeq      'N'                  
C                   EXSR      CHKADR_SR            

4 个答案:

答案 0 :(得分:5)

Buck和Benny就如何改进RPG代码提出了很多好的建议。

  • 使用免费格式提高可读性
  • 使用较长的描述性名称,以阐明实际情况。 (当你刚开始给出明确的名字时,不要让某人必须给你起名字)
  • 使用子程序而不是子程序。从子过程返回一个值使其成为用户定义的函数,甚至更好

程序应该执行一个想法。该程序中的所有内容都应该与做那件事情有关。这称为cohesion。保持您的程序相当小而简单。多么小。作为麻省理工学院人工智能实验室负责人的Semour Papert有兴趣让年轻学生对计算机进行编程以做他们感兴趣的事情。当他问其中一个人他们认为一个程序应该有多大时,他得到的答案是“心灵大小的咬合”。

您希望最小化过程之间不必要的依赖关系,以便一个区域中的更改不太可能导致另一个区域出现问题。这称为coupling

在你的循环中,注意你检查“A”或“C”的位数,并为“A”重复相同的代码块,然后再为“C”重复一次。您可能已经使用了IF .. OR ..因此您不会重复代码块,这可能导致将来出现维护问题。它违反了DRY原则,不要重复自己。你可以把自己想象成一个整洁的管家说,有一个地方可以解决所有问题[代码行],以及所有代码[代码行]。

现在到了一个小问题。到处都是,我看到人们使用相同的键立即使用SETLL,接着是READE。请改用CHAIN。在封面下,CHAIN执行SETLL完成的逻辑,然后执行READE的逻辑。有些人认为READE以成功的SETLL为条件,可以节省时间。但正在发生的是,对于每个I / O语句,编译器生成代码以准备调用I / O模块的参数,调用模块执行I / O函数,然后处理返回的参数。有两个I / O语句,你这两次。让CHAIN操作为您处理它,它也有机会获得一些内部效率。此外,您的RPG代码现在更简单一些。从各个角度来看都会更好。

为此做好准备......

您应该使用嵌入式SQL,而不是使用传统的I / O语句。有很多原因,我真的不想在这里写一整篇论文。现在就相信我。学习这项工作的投入确实得到了回报。

您将学习SELECT,DECLARE和OPEN CURSORS(如开放数据路径),然后从光标获取FETCH记录,甚至一次FETCH或INSERT多条记录。

现在,真的很重要

传统的RPG范例通常是循环,每次通过循环处理一条记录,通常一次在一条记录上对其他文件执行额外的I / O.

SQL使您可以将记录作为一个集合在一个语句,整个文件或多个文件中处理。您可以通过在单个SQL语句中运行整个文件以及其他两个来简化和简化 dramaticallly

CREATE TABLE QTEMP/PURGING AS
( SELECT c.customer, ...
    FROM Customers c
    LEFT EXCEPTION JOIN Orders o
            on c.customer = o.customer
    LEFT EXCEPTION JOIN Invoices i
            on c.customer = i.customer
    WHERE c.customer not in 
           (select s.customer
              from secondfile s
              where marketing in ('A','C')
                and eventdate > '2007-12-31'
           )
) with data;

DELETE FROM secondfile x
  WHERE x.customer in
          (select p.customer
             from purging p
          );

DELETE FROM thirdfile x
  WHERE x.customer in
          (select p.customer
             from purging p
          );

DELETE FROM Customers x
  WHERE x.customer in
          (select p.customer
             from purging p
          );

四个陈述。这就是全部。没有循环。一旦你得到第一个正确的陈述,剩下的就很简单了。

CREATE TABLE ... WITH DATA将SELECT的结果写入新表。我只是展示它进入QTEMP,因为我不确定你是否想保留它。 LEFT EXCEPTION JOIN表示使用左侧表格中的行,右侧没有与搜索条件匹配的行。这使您可以选择不在订单历史记录中而不在发票文件中的客户记录。一旦构建了包含要清除的客户列表的文件,就可以使用该列表从Customer Master和其他两个文件中删除这些客户。

答案 1 :(得分:3)

我写了一个函数(子程序)。局部变量使得这样的代码更容易使用,因为您不会与主线中的变量发生冲突。我发现创建一个函数可以帮助我组织我的想法,并且通过有组织的想法,我会编写更好的代码。 “更好”当然是主观的,但在这里我的意思是更容易理解,更重要的是,更容易改变而不会在过程中破坏其他东西。

变量名......哦。使用更长的名称 - 有意义的名称。 ACFLAG会让你的生活更糟糕,下次你要看这个(也许是一年?七年?)我更喜欢Benny的do_purge - 它说明了它的意图。它可能是一个指标变量,可以确定它是一个是/否的决策点,但理解if do_purge = 'Y'比理解if acflag = 'N'更容易理解。负逻辑增加了问题。子例程遭受相同的神秘命名约定。检查客户。检查一下?正在实施的业务功能是什么?如果名称不能轻易描述,那就太复杂了 - 做太多事情。业务功能是“检查活跃客户”吗?用那个方式命名子程序(或者更好的是,用这种方式写出函数名)。你的主线可能会变成

if custIsInactive(customerID);
  exsr purge_customer;
endif;

评论。班尼在他必须合作的工作上做得很好。原始代码只有一条评论,而且几乎完全没有用。我们可以看到找到了订单实体 - 这就是if not %eof()的含义。而且我们也可以看到我们要写一条记录。但是没有解释为什么这些行为是重要的,可取的和有用的。这里的东西对我很有帮助。先写下评论。不是伪代码!宇宙历史上最糟糕的评论如下:

// if X > 10, reset y
if x > 10;
  y = 0;
endif;

这条评论只会让人分心。白色空间会更好。简单的代码不需要评论。更复杂的代码总是受益于解释意图的注释。什么样的评论会有助于这段代码?用英语解释为什么代码A和C很重要。也许是因为

// don't purge customers we've done business with
// exception: if we emailed or cold called them
//            and they didn't buy anything in the 
//            past 6 years, purge them.
if (bicotc = 'A' and bistpd >= 20070101) or
   (bicotc = 'C' and bistpd >= 20070101);
  do_purge = 'Y';
endif;

我意识到发布的代码没有这样做,但从玻璃的这一方面我无法分辨你是否想要它的编写方式,或者它是一个我们只是避风港的错误还没跳过。评论应澄清 intent 。相信我,当下一个人进入这个代码时,他会很高兴阅读代码A和C的简单英语原因,以及为什么特定日期很重要。也许这是合并或收购的日期,A和C项目来自旧部门......代码原样并没有解释。

如果评论不赞成(并且他们在某些地方),至少要避免“魔法代码”和“魔术数字”。怎么样:

if (bicotc = OHIO_BIG_TICKET and bistpd >= MERGER_DATE) or
   (bicotc = OHIO_LITTLE_TICKET and bistpd >= MERGER_DATE);
  do_purge = 'Y';
endif;

最后,回到一次做一件事的概念。这个“检查客户”子程序显然不仅仅是“检查” - 它正在向VRCSTKBI写入记录。根据情况描述,这看起来像是一个错误。基于setll / reade循环,出现代码正在查看订单历史记录文件。此子例程将为前10个项目写入VRCSTKBI,然后第11个使客户不符合清除条件。但VRCSTKBI中有该客户的记录。哎呦。通常,我们很想以效率的名义将多个I / O操作捆绑在一起。我在这里告诉你,经过35年的努力,我同意唐纳德克努特的观点:

  

“我们应该忘记效率低,大约97%的时间说:   过早优化是万恶之源。“

考虑业务功能。

  1. 客户是否有资格被清除?
  2. 记录要清除的客户。
  3. 从女儿档案中清除客户。
  4. 如果每个业务操作都有单独的功能,那么将来编写,理解测试,调试和修改会更容易。编写3个子程序,用好名字命名,然后将它们部署在主线中:

    if custIsInactive(customerID);
      record_purged_customerID(customerID);
      purge_customer(customerID);
    endif;
    

    函数返回信息(custIsInactive)或提供服务(purge_customer)。在“提供服务”功能中不应该有太多的业务逻辑做出决策,并且提供信息的功能不应该是实现服务。不是因为混合和匹配本质上是邪恶的,而是因为一段代码所做的事情越多,理解和维护就越难。我们只能在我们的活动内存中保留少量内容,因此将业务功能抽象为单个项目(函数名称)的能力对于制作健壮的程序非常有用。

答案 2 :(得分:2)

您只需要客户专栏就可以消除暗示您选择更多列的三个点。

通过添加distinct关键字使清除表唯一,然后您可以使用三个更易读的异常连接,因为分析不必从异常连接转换到不在。如果删除肯定会更快临时清除表有一个客户主键。

为了以防万一,我还会添加一个删除表。

drop table qtemp/purging;

CREATE TABLE QTEMP/PURGING AS
( SELECT distinct c.customer
FROM Customers c
LEFT EXCEPTION JOIN Orders o
        on c.customer = o.customer
LEFT EXCEPTION JOIN Invoices i
        on c.customer = i.customer
left    exception join secondfile s on 
        c.customer = s.customer and
          marketing in ('A','C')
            and eventdate > '2007-12-31'
       )
) with data;

答案 3 :(得分:1)

这是你想要的吗?

/free
    if pugfil = 'Y' and agent# <> agent#_old;
       exsr chkcus_sr;
       if do_purge = 'Y';
          write trcmasrr;
          delete arcmasrr;
       endif;
    endif;

    begsr chkcus_sr;
       // Assume we will purge this customer.
       do_purge = 'Y';
       setll orhkey drcst1;
       reade orhkey drcst1;
       dow not %eof(drcst1);
          // If bicotc is A or C and the date is January 1, 2007 or later, do NOT purge and stop checking other records.
          if (bicotc = 'A' and bistpd >= 20070101) or
             (bicotc = 'C' and bistpd >= 20070101);
             // Make sure you change the flag to say NO - DON'T PURGE THIS CUSTOMER
             do_purge = 'N';
             leavesr;
          endif;
          write vrcstkrr;
          // Looks like you are doing more processing here but you don't show the code...
          reade orhkey drcst1;
       enddo;
    endsr;
 /end-free

或者如果你想坚持使用固定格式:

c                   ifeq      pugfil = 'Y' and
c                             agent# <> agent#_old
c                   exsr      chkcus_sr
c                   if        do_purge = 'Y'
c                   write     trcmasrr
c                   delete    arcmasrr
c                   endif
 *------------------------------------------------------------------
c     chkcus_sr     begsr      
c                   eval      do_purge = 'Y'
c     orhkey        setll     drcst1
c     orhkey        reade     drcst1
 * If the order entity is found, write the rec into VRCSTKBI file
c                   dow       not %eof(drcst1)
c                   if        (bicotc = 'A' and bistpd >= 20070101) or
c                             (bicotc = 'C' and bistpd >= 20070101)
c                   eval      do_purge = 'N'
c                   leavesr
c                   endif

c                   write     vrcstkrr
 * // Do some other processing here that you don't show...
c     orhkey        reade     drcst1
c                   enddo