If you have any comments on the content or layout of this page please
let me know.
| Tip | Date Published | Description |
|---|---|---|
|
|
|
Natural source comparisons |
|
|
|
Monthly date calculations |
|
|
|
Dynamic report distribution |
| 6 | 07/14/1997 | Natural batch restartability |
| 5 | 07/07/1997 | Sequential match |
| 4 | 07/07/1997 | READ syntax |
| 3 | 07/05/1997 | Replacing certain AT BREAKs with a SORT |
| 2 | 06/30/1997 | Continuation Symbol
Here are some uses you may not have thought of. |
| 1 | 06/10/1997 | Initialization of an SPDE |
Return to Ralph G. Zbrog's Home page.
Tip 9 02/28/1998 Compare Natural source
There is no need to purchase additional software products to compare two Natural source members if you have MVS/TSO. Just download ZZUTILs. Then use the ZPUNCH utility to extract Natural source to a flat file, and IBM's SuperC comparison utility to tell you what the differences are.
ZZCOMPR is a Natural program which submits
a batch "compare" job from on-line via SAG's NATRJE facility. If
you need to submit the jobs manually from TSO, extract the embedded JCL
from ZZCOMPR and customize it as necessary.
Top of Tips & Techniques. Previous tip. Top of this tip.
Return to Ralph G. Zbrog's home page.
Tip 8 11/04/1997
Monthly date calculations
Often we are called upon to compute dates which are one, two, and three
months into the past, for aging report purposes. Sometimes we need
to know how many individuals in a file will reach their twenty-first birthday
within the next six months. In these and countless other situations,
date calculations are being made based on a number of months. For
a quick estimate, we could multiply the number of months by thirty and
then add or subtract this number from a D-format value. For a more
accurate computation, use ZZMONTH.
ZZMONTH is a Natural subprogram. Pass it a date and a number of months and it returns a new date and a date range. The new date will be in the future or past depending on whether the number of months is positive or negative, respectively. The date range will be the first day of the month and last day of the month with respect to the new date.
For example
CALLNAT 'ZZMONTH' #GIVEN
#MONTHS
#EXACT
#FROM
#THRU
If #GIVEN contains D'11/04/1997' and #MONTHS contains 3, upon return #EXACT will contain D'02/04/1998', #FROM will contain D'02/01/1998', and #THRU will contain D'02/28/1998'. If #GIVEN contains D'11/04/1997' and #MONTHS contains -3, upon return #EXACT will contain D'08/04/1998', #FROM will contain D'08/01/1998', and #THRU will contain D'08/31/1998'.
The source for ZZMONTH includes an on-line mainline to help you test
the subprogram. Try setting #GIVEN to D'11/30/1995' and #MONTHS to
3. Then change #GIVEN to D'11/30/1997' or D'02/28/1997'.
Top of Tips & Techniques. Most recent tip. Previous tip. Top of this tip. Next tip.
Return to Ralph G. Zbrog's home page.
Tip 7 11/04/1997
Report Distribution
Say you have a report which has a page break by Region Code.
Each region's pages should be printed on that region's printer. In
addition the head office wants a complete set of the regional report.
Prior to the DEFINE PRINTER statement it was common to write one program
for head office and one for each region, or to break the data by region
into subset datasets, reporting each dataset individually.
With the DEFINE PRINTER statement you need pass the data only once. Use WRITE(xx) for the head office report (invariably I use WRITE(01) for this guy); you don't need a DEFINE PRINTER(xx). Use WRITE(yy) for the regional reports (WRITE(02) seems pretty logical to me). Create a subroutine to determine dynamically to which printer to route the current regional report. Perform the routine at start of data (don't assume the first data record is for the first region) and at break of region.
RESET *PAGE-NUMBER(yy)
DECIDE ON FIRST #REGION
VALUE 'a1'
DEFINE PRINTER(yy) OUTPUT 'CMPRT02'
VALUE 'b2'
DEFINE PRINTER(yy) OUTPUT 'CMPRT03'
...
In your JCL, CMPRT02 and CMPRT03, etc, will point to specific regional printers.
You will want the WRITE(yy) TITLE statement to contain the word REGIONAL while the WRITE(xx) TITLE statement reflects its broader scope. There is no need for a NEWPAGE(yy) statement. Execution of the DEFINE PRINTER implies a new page.
The only difference in the detail line WRITE statements is the printer designation. The field lists are identical.
Of course you still have a problem if you need more than a total of 31 report destinations. That's Natural's limit.
You can see sample code and JCL here.
I would like to thank Ray Bergen for this
tip. Although I knew it was possible to do this in theory, Ray was
the one who came up with the right combination of PERFORM, DECIDE, and
OUTPUT specifications.
Top of Tips & Techniques. Most recent tip. Previous tip. Top of this tip. Next tip.
Return to Ralph G. Zbrog's home page.
Tip 6 07/14/1997
Natural Batch Restartability
ZZRESTRT is a source module which demonstrates
how to code a restartable Natural program. It is only a stub, requiring
significant customization. Optional code is identified with comments.
ZZRESTRT includes the following features:
When a program ABENDs, all updates which have not been committed are backed out automatically, but all work records remain on the sequential output data set. When the program is restarted, the backed out transactions are re-processed and duplicate work records are created (in the JCL the new records are appended to the original data set). When the data set is sorted for reporting, as suggested above, the SEQuence field is used to eliminate the duplicates. In DFSORT and SYNCSORT this is done by including the SEQuence field in the low order of the sort and including SUM FIELDS=NONE.
Also note the placement of the final WRITE WORK and summary audit record
prior to the final ET. This placement is intentional. Even
if the program ABENDs after the last ADABAS record has been processed,
program execution will be in restart mode and the final totals will be
correct.
Top of Tips & Techniques. Most recent tip. Previous tip. Top of this tip. Next tip.
Return to Ralph G. Zbrog's home page.
Tip 5 07/07/1997
Sequential match
When programmers move from a 3GL to Natural, one of the first techniques
to go is the sequential match - the process of reading two files (invariably
a master file and a transaction file) sequentially, comparing their keys,
and processing matches, widows (masters with no transactions), and orphans
(transactions with no master). There are two reasons for this.
First, it is just too easy to code a READ of the master file with a nested
READ or FIND of the transaction file. Second, it is not possible
in Natural to code a true sequential match of two ADABAS files, because
the second loop must be closed before the outer loop can be reiterated.
The best you can do is code a step-sequential match (as was already described
as a READ with a nested READ or FIND).
The step-sequential method has several problems:
The benefits of this approach:
A case study:
The program was rewritten as a series of extracts, sorts, and sequential matches. Physical READs and Prefetch were used extensively. Even after the data base had grown to 300,000 masters in a data base of six million records, the report ran consistently in two hours.
Return to Ralph G. Zbrog's home page.
Tip 4 07/07/1997
READ syntax
In the READ statement I recommend using the BY keyword instead of WITH
and the FROM keyword instead of the equal sign ("="). I'll explain
by using a real-life example.
A programmer coded the following
I too have changed FINDs to READs and vice versa, but I always take the time to change the WITH/BY and "="/FROM keywords.
I think Software AG made an error in equating FROM and "=" in the READ
statement's syntax.
Top of Tips & Techniques. Most recent tip. Previous tip. Top of this tip. Next tip.
Return to Ralph G. Zbrog's home page.
Tip 3 07/01/1997
Replacing certain AT BREAKs with a SORT
I like the AT BREAK statement. Despite some tricky characteristics,
and some quirks which make complicated situations even more difficult to
code (I'll leave those for a future Tip), I prefer it to the old 3rd-generation
method of saving and comparing key values. It is well worth the effort
of learning how to use it.
Coding can be as simple as
This tip deals with replacing the COUNT and SUM functions. The example which benefits most from this technique is the one in which no appropriate descriptor exists, so the SORT statement or SORT utility must be used. I'll defer a detailed discussion to a future Tip, but suffice it to say that I always prefer a separate SORT step to an internal sort. This changes the sample code above to
SORT step:
SORT FIELDS=(1,2,A, STATE CODE
3,3,A), COUNTY CODE
FORMAT=CH
READ WORK 1
AT BREAK OF #STATE-COUNTY
WRITE OLD(STATE)
OLD(COUNTY)
COUNT(TRAN-AMT)
SUM(TRAN-AMT)
END-BREAK
I haven't explained where the COUNT field in position 11 of the WORK record came from. Either add a line containing '00001' to the WRITE WORK statement, or, as I prefer, add an INREC statement to the SORT parameters.
There are other examples where AT BREAK was still necessary, but coding
was simplified by using the SUM and INREC statements of the SORT utility.
Top of Tips & Techniques. Most recent tip. Previous tip. Top of this tip. Next tip.
Return to Ralph G. Zbrog's home page.
Tip 2 06/30/1997 Continuation Symbol
The continuation symbol (the hyphen '-') is used to code literals which cannot fit on one Natural source line, as in
....+....1....+....2....+....3....+....5....+....6....+....7..
1 #A(A100) INIT<'This is a literal
which cannot fit on a '
- 'single, 72-character, Natural
source line.'>
I have four other uses for the continuation symbol.
You often see code similar to this
....+....1....+....2....+....3....+....5....+....6....+....7..
IF some-condition
THEN
REINPUT
'A long message that won"t line up properly'
ALARM
END-IF
but I prefer
....+....1....+....2....+....3....+....5....+....6....+....7..
IF some-condition
THEN
REINPUT 'A long message that won"t '
- 'line up properly'
ALARM
END-IF
Or going back to the first example
....+....1....+....2....+....3....+....5....+....6....+....7..
1 #A(A100) INIT<'This is a literal
which cannot fit on a '
- 'single, 72-character, Natural source line.'>
Starting value in a READ by superdescriptor
If you're writing an ad hoc to read a superdescriptor, there is no need for the definitions, redefinitions, and assignments typically associated with the READ of a superdescriptor in a production program. You can specify the starting value within the READ statement itself.
....+....1....+....2....+....3....+....5....+....6....+....7..
1 #SUPER-DEF
2 #STATE(A2)
INIT<'CA'>
2 #COUNTY(P3)
INIT<345>
1 REDEFINE #SUPER-DEF
2 #SUPER(A5)
...
READ view BY ST-CTY FROM #SUPER
becomes
....+....1....+....2....+....3....+....5....+....6....+....7..
READ view BY ST-CTY FROM 'CA' - H'345F'
If the superdescriptor contains a D-format field, use a utility like
ZZDATES to determine the packed value to be specified
in the READ.
Initialize internal tables
Take the superdescriptor example, above, a step further to initialize
an array.
....+....1....+....2....+....3....+....5....+....6....+....7..
1 #MONTHS
2 #MTH(A3/12)
INIT<'Jan', 'Feb', 'Mar', 'Apr',
'May', 'Jun', 'Jul', 'Aug',
'Sep', 'Oct', 'Nov', 'Dec'>
2 #DAYS(N2/12)
INIT<30, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31>
It's easier to see the relationship of "columns" to "rows" like this:
....+....1....+....2....+....3....+....5....+....6....+....7..
1 #MONTHS(A5/12) INIT<'Jan' -
'30',
'Feb' - '28',
'Mar' - '31',
'Apr' - '30',
...
'Dec' - '31'>
1 REDEFINE #MONTHS
2 #ELEMENT(12)
3 #MTH(A3)
3 #DAYS(N2)
For a real-life example of this technique, take a look at the ZZUtils
menu program.
Is the following Natural code valid? If not, why not? If so, under what conditions, and what, if any, are the results of execution?
....+....1....+....2....+....3....+....5....+....6....+....7..
ASSIGN #A = '456' - '123'
ASSIGN #B = 'DEF' - 'ABC'
Does #A contain '333'? What about #B? Solution.
Top of Tips & Techniques. Most recent tip. Previous tip. Top of this tip. Next tip.
Return to Ralph G. Zbrog's home page.
Tip 1 06/10/1997 Initialization of a superdescriptor
All too often, superdescriptor starting values are initialized at execution time.
....+....1....+....2....+....3....+....5....+....6....+....7..
1 #SUPER(A4) 1 REDEFINE #SUPER
2 #STATE(A2)
2 #COUNTY(P3)
...
ASSIGN #STATE = 'CA'
ASSIGN #COUNTY = 345
READ view BY ST-CTY FROM #SUPER
I prefer compile-time initialization.
....+....1....+....2....+....3....+....5....+....6....+....7..
1 #SUPER-DEF
2 #STATE(A2)
INIT<'CA'>
2 #COUNTY(P3)
INIT<345>
1 REDEFINE #SUPER-DEF
2 #SUPER(A4)
...
READ view BY ST-CTY FROM #SUPER
Top of Tips & Techniques. Most recent tip. Top of this tip. Next tip.
Return to Ralph G. Zbrog's home page.
Last updated April 25, 1998, by Ralph
G. Zbrog.
Assuming both #A and #B are of format/length A6, #A contains '456123' and #B contains 'DEFABC'.
Return to Tip 2.