0

--- 在这里,我们找到了要在其他文件中清除的客户编号。首先我们正在读取客户主数据,然后我们在订单历史记录或发票历史记录中查看客户编号是否存在。如果不是,那么我们想从客户主文件以及其他 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

4 回答 4

5

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

  • 使用自由格式提高可读性
  • 使用更长的描述性名称,以阐明事物的实际含义。(当你可以刚开始给出明确的名字时,不要让别人不得不破译一个名字)
  • 使用子程序而不是子程序。从子过程返回值使其成为用户定义的函数,甚至更好

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

您希望最大限度地减少过程之间不必要的依赖关系,以便一个区域的更改不太可能导致另一个区域的问题。这称为耦合

在你的循环中,注意你检查了多少个地方是“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 或 INSERT 多条记录。

现在,真正的大事

传统的 RPG 范式一般是通过循环,每次循环处理一条记录,通常对其他文件一次执行一条记录的附加 I/O。

SQL 使您能够在单个语句中、整个文件或多个文件中将记录作为一个集合进行处理。通过在单个 SQL 语句中运行整个文件以及其他两个文件,可以显着减少和简化您的 RPG 代码。

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 表示使用左侧表中的行,右侧没有与搜索条件匹配的行。这使您可以选择不在订单历史记录和发票文件中的客户记录。一旦构建了包含您要清除的客户列表的文件,您就可以使用该列表从客户主文件和其他两个文件中删除这些客户。

于 2013-09-05T04:56:31.237 回答
3

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

变量名...哦。使用更长的名字——有意义的名字。下次你必须看这个时(可能一年?七年?),ACFLAG 会让你的生活变得更糟。我更喜欢 Benny's 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;

我意识到发布的代码并没有这样做,但是从玻璃的这一面来看,我无法判断您是否打算按照它的编写方式进行操作,或者这是一个我们尚未解决的错误。评论应阐明意图。相信我,当下一个人读到这段代码时,他会很乐意阅读代码 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 年之后,我同意唐纳德·高德纳 (Donald Knuth) 的观点:

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

考虑业务功能。

  1. 客户是否有资格被清除?
  2. 记录要清除的客户。
  3. 从子文件中清除客户。

如果您对每个业务操作都有单独的功能,那么将来编写、理解测试、调试和修改会更容易。编写3个子过程,用好名字命名,然后部署到主线:

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

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

于 2013-09-05T01:10:04.607 回答
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;
于 2013-09-11T17:20:02.073 回答
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
于 2013-09-04T21:00:59.167 回答