---这里我们找到了我们要在其他文件中清除的客户编号。首先,我们正在阅读客户主数据,然后在订单历史记录或发票历史记录中查看客户编号是否存在。如果不是,那么我们希望从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
答案 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%的时间说: 过早优化是万恶之源。“
考虑业务功能。
如果每个业务操作都有单独的功能,那么将来编写,理解测试,调试和修改会更容易。编写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