• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Writing Control Break Programs

    May 12, 2004 Ted Holt

    [The code for this article is available for download.]

    Having worked on programs written by different programmers, I have noticed a lot of different techniques and programming styles. However, one thing is consistent: that most programmers do not design programs. When it comes time to write a program, they just sit down at their workstations and have at it. This approach to program development results in error-prone programs that are hard to modify. A saying I once read on a poster comes to mind: If builders built houses the way programmers write programs, the first woodpecker to come along would destroy civilization.

    Control-break programs are good examples of this lack of design. It’s often obvious that the programmer of a control-break program did not design the program, but instead cobbled something together that produced the desired results.

    Since I’m no longer allowed to use my beloved RPG cycle, I have developed a methodical way to write procedural control-break programs that not only produce the desired results but also are easy for me to read, understand, modify, and debug. Since I work in RPG, I will use that language to illustrate my method. However, I have used the same logic just as effectively in other procedural languages.

     

    THE TEMPLATE

     

    First let me present the template, cbtemplate.rpg, from which I begin. Tokens surrounded by angle brackets (< and >) are to be replaced with proper values, such as field and file names. The template has logic for three levels of control breaks. Level 10 is the major break field, level 20 is an intermediate break, and level 30 is the minor break. You may need to add or delete levels, but doing so is a matter of copying the code for a level and modifying it appropriately.

    As I explain the principles behind control-break logic, I will illustrate with parts of the template.

    Principle 1: You must save the values of control fields in order to check for control breaks. Keep in mind that a control break may have more than one control field. If your control fields can be null, you will have to save the null value setting, in addition to the field value. Here are the save fields from the template.

     * control break save fields
    D cb10            s                   like(<major.break.field>)
    D cb10Save        s                   like(cb10)
    D cb20            s                   like(<intermediate.break.field>)
    D cb20Save        s                   like(cb20)
    D cb30            s                   like(<minor.break.field>)
    D cb30Save        s                   like(cb30)
    
    D Null10          s              5i 0
    D Null10Save      s                   like(Null10)
    D Null20          s              5i 0
    D Null20Save      s                   like(Null20)
    D Null30          s              5i 0
    D Null30Save      s                   like(Null30)
    

    This code makes two assumptions. The first is that the break fields can be null. If that is not the case, you can remove the null fields from this section of code and anywhere else they’re used. The second assumption is that there is only one control field per level. If there are two or more control fields on one level, duplicate the variable names for that level and modify them. I like to insert a letter after the level number. For example, if level 30 has two control fields, I would define variables CB30A, CB30ASAVE, CB30B, CB30BSAVE, and so on.

    Principle 2: Load the comparison fields when you successfully read an input record. Here is part of the routine to read an input record. My template is set up to use SQL, but this logic works just as well with native I/O.

    C     Read          begsr
    C
    C/exec SQL
    C+    Fetch c1 into <host variable list here>
    C+
    C/end-exec
    C
    C                   eval      bEOF = (SQLStt <> SQLNormal)
    C
    C                   if        not bEOF
    C* copy input fields to to control break variables here
    C                   eval      Null10 = <major.break.field.null.ind>
    C                   eval      Null20  = <intermediate.break.field.null.ind>
    C                   eval      Null30 = <minor.break.field.null.ind>
    C                   if        Null10 >= *zero
    C                   eval      cb10 = <major.break.field>
    C                   endif
    C                   if        Null20 >= *zero
    C                   eval      cb20 = <intermediate.break.field>
    C                   endif
    C                   if        Null30 >= *zero
    C                   eval      cb30 = <minor.break.field<
    C                   endif
    C                   endif
    C
    C                   endsr
    

    If the program reads a record, the SQL status code is all zeros, which is equivalent to the constant SQLNormal, defined in the D-specs. SQL returns a status 02000 when it runs out of data, forcing variable BEOF to be true. If SQL finds a record, it updates the control-break variables with the field values and null indicators.

    Principle 3: You must check for the control breaks in major to minor order before processing an input record. In the following code snippet, level 10, the major break, is tested first. If no control break has occurred, the next level is tested.

    C                   select
    C                   when      cb10 < > cb10Save
    C                              or Null10 <> Null10Save
     ... calcs for highest level break go here
    C                   when      cb20 <> cb20Save
    C                              or Null20 <> Null20Save
     ... calcs for 2nd-highest level break go here
    C                   when      cb30 <> cb30Save
    C                              or Null30 <> Null30Save
    ... calcs for lowest level break go here
    C                   endsl
    

    Principle 4: When a control break occurs, end the preceding group before beginning the new one.

    The following subroutine is a fleshed-out version of the previous code segment.

    C     CheckCtlBreak begsr
    C
    C                   select
    C                   when      cb10 <> cb10Save
    C                              or Null10 <> Null10Save
    C                   exsr      EndLvl10
    C                   exsr      BgnLvl10
    C                   when      cb20 <> cb20Save
    C                              or Null20 <> Null20Save
    C                   exsr      EndLvl20
    C                   exsr      BgnLvl20
    C                   when      cb30 <> cb30Save
    C                              or Null30 <> Null30Save
    C                   exsr      EndLvl30
    C                   exsr      BgnLvl30
    C                   endsl
    C
    C                   endsr
    

    The subroutine that ends a control group is called before beginning the next control group.

    Principle 5: Define subroutines or subprocedures to process the beginning and end of each control break. Using routines makes it easy to ensure that everything is done at the proper time. Here is the template subroutine to begin a level.

    C     BgnLvl10      begsr
    C
     * save control break fields
    C                   eval      cb10Save = cb10
    C                   eval      Null10Save = Null10
     * print headings for this level
     * clear accumulators for this level
     * force breaks at lower levels
    C                   exsr      BgnLvl20
    C
    C                   endsr
    

    Notice the comments. They tell you what needs to be done. All you have to do is add the proper code after each comment, if applicable. The method I use requires that I save control-break values, print headings for the group, clear accumulators and counters for the group, and force the next lower level.

    Principle 6: Major breaks always force minor breaks. Suppose you are breaking on date within department number. If department number changes, it doesn’t matter whether date changes. You must force a date break if there is a break on department number.

    Continuing with the template, here’s a subroutine to end a level.

    C     EndLvl10      begsr
    C
     * force breaks at lower levels
    C                   exsr      EndLvl20
     * accumulate to higher levels
     * print totals
    C
    C                   endsr
    

    When ending a level, you must force the next lowest level before doing anything else. As with the beginning of a control group, fill in the necessary calculations after the comments. In the typical program, the end of a group is the best place to accumulate to higher levels and print group totals.

    Principle 7: You must force the highest level control break before the first record and after the last record of the input dataset. Besides the routines for each control-break level, you will need routines for the beginning and end of the input. I refer to these as level 00. Their primary purpose is to force the highest level break, but you may put other beginning-of-file and end-of-file tasks in these subroutines.

    Principle 8: In the detail routine, which processes each input record, accumulate to the lowest control break level. This principle could probably be omitted, since the detail processing is the only place to accumulate to the lowest level anyway.

     

    THE REST IS DETAILS

     

    That’s an overview of what it takes to handle control breaks. Using this method, you can easily handle any number of break levels. You will need to determine how you’re going to build output. I used to use externally described printer files, but lately I’ve been using the method Cletus the Codeslinger described in “An Alternative to Externally Described Printer Files.” Cletus’ method works better because it ensures that column headings, detail data, and control-level accumulators are aligned.

    To help you see how it all fits together, here’s an example program from the template. The example is made up of five source members:

     

    Member Type
    CBEXAMPLE2 SQLRPGLE
    CBPRINTF PRTF
    INVDTL PF
    INVHDR PF
    PSDS RPGLE

     

    For an idea of the output, view the report.txt file. You may have to modify bits and pieces of it. For example, there is a /COPY directive to put the PSDS member into the CBEXAMPLE2 member. Make sure it points to the proper location of PSDS.

     

    THERE’S NO SUBSTITUTE FOR DESIGN

     

    A program I had to work on recently had two level breaks, one was done with the L1 level indicator, the other by comparing to a save field. It was not an easy program to modify, but that’s what comes of a lack of design. Since control-break reports are so common in business programming, I encourage you to adopt a control-break design that everyone in your shop can use.

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    WorksRight Software

    Do you need area code information?
    Do you need ZIP Code information?
    Do you need ZIP+4 information?
    Do you need city name information?
    Do you need county information?
    Do you need a nearest dealer locator system?

    We can HELP! We have affordable AS/400 software and data to do all of the above. Whether you need a simple city name retrieval system or a sophisticated CASS postal coding system, we have it for you!

    The ZIP/CITY system is based on 5-digit ZIP Codes. You can retrieve city names, state names, county names, area codes, time zones, latitude, longitude, and more just by knowing the ZIP Code. We supply information on all the latest area code changes. A nearest dealer locator function is also included. ZIP/CITY includes software, data, monthly updates, and unlimited support. The cost is $495 per year.

    PER/ZIP4 is a sophisticated CASS certified postal coding system for assigning ZIP Codes, ZIP+4, carrier route, and delivery point codes. PER/ZIP4 also provides county names and FIPS codes. PER/ZIP4 can be used interactively, in batch, and with callable programs. PER/ZIP4 includes software, data, monthly updates, and unlimited support. The cost is $3,900 for the first year, and $1,950 for renewal.

    Just call us and we’ll arrange for 30 days FREE use of either ZIP/CITY or PER/ZIP4.

    WorksRight Software, Inc.
    Phone: 601-856-8337
    Fax: 601-856-9432
    Email: software@worksright.com
    Website: www.worksright.com

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    PFSweb Launches E-Commerce App for iSeries Shops i5 Announcements Loaded with Software, Previews

    Leave a Reply Cancel reply

Volume 4, Number 16 -- May 12, 2004
THIS ISSUE
SPONSORED BY:

Advanced Systems Concepts
Guild Companies
WorksRight Sofware
*noMAX
Client Server Development

Table of Contents

  • Tomcat 5 and DB2 for iSeries
  • Writing Control Break Programs
  • Changing Prototypes and Dual Prototyping
  • Sending E-Mail from RPG
  • OS/400 Alert: i5, or iHype?

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • Meet The Next Gen Of IBMers Helping To Build IBM i
  • Looks Like IBM Is Building A Linux-Like PASE For IBM i After All
  • Will Independent IBM i Clouds Survive PowerVS?
  • Now, IBM Is Jacking Up Hardware Maintenance Prices
  • IBM i PTF Guide, Volume 27, Number 24
  • Big Blue Raises IBM i License Transfer Fees, Other Prices
  • Keep The IBM i Youth Movement Going With More Training, Better Tools
  • Remain Begins Migrating DevOps Tools To VS Code
  • IBM Readies LTO-10 Tape Drives And Libraries
  • IBM i PTF Guide, Volume 27, Number 23

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle