define data local
1 #name (a20)
1 #first (a20)
1 myview view of employees
2 name
2 first-name
end-define
read myview by name starting from ‘G’ to ‘GEE’
at start of data
move name to #name
move first-name to #first
accept if name = ‘qwert’
end-start
*
display #name#first
move name to #name
move first-name to #first
at end of data
display #name#first
write ‘this is the last record’
end-enddata
end-read
end
Here is what is going on.
When the first record is read it goes to the at start of data code. The first record will NOT be processed now. Instead, the first record is “moved” to a hold area (#NAME and #FIRST). The “silly” accept statement is designed as an unconditional escape top. HOWEVER, it turns out you cannot do ESCAPE TOP from within an AT START OF DATA, whereas you can do accept/reject there.
Okay, now we read the second record. We “process” the first record (the display of the hold area), then we move the second record to the hold area.
We continue in this manner; always processing the “last record read” then moving the current record read to the hold area.
At the end of the range, in the at end of data clause, we process the “last record” which is in the hold area.
You should be able to adapt this code to your needs.
The “problem” with just an AT END OF DATA clause is that you you have already processed the last record when you discover that you have reached the end of your data.
By putting the record processed out of synch with the last record read, I know (when I am in the AT END OF DATA clause) that the hold area is the last record before I begin its processing.
Thanks, Steve. I had tried using just the AT END OF DATA statement and, as you say, it was processing the last record but not displaying it. Your “out-of-sync” method makes sense. I’ll give it a try!
Hi Dan,
Perhaps, I’ m not doing what you really need, but this is the program and its result; I think I`d rather agree with Mattias; perhaps, you simply forgot to put Display statement within your AT END OF DATA clause?
Best regards,
NK
DEFINE DATA LOCAL
1 EDB VIEW OF EMP-ADA
2 NO-MATR
END-DEFINE
READ EDB BY NO-MATR STARTING FROM ‘100008855’ TO ‘100081000’
DISPLAY EDB.NO-MATR ‘RECORD PROCESSED’
AT END OF DATA
WRITE ‘THE LAST DATA PROCESSED AND DISPLAYED’ EDB.NO-MATR
END-ENDDATA
END-READ
END
100008855 RECORD PROCESSED
100081000 RECORD PROCESSED
THE LAST DATA PROCESSED AND DISPLAYED 100081000
When I code it using just the AT END OF DATA clause, I don’t get my ‘LAST PAGE HAS BEEN DISPLAYED’ message when I reach the last record. If I hit NEXT again after the last record, then I get the displayed message. However, it works if I use Steve’s out-of-sync method. I’ll post the two versions of the code. First, just the straight AT END OF DATA method:
DEFINE DATA LOCAL
1 LOGFILE VIEW OF LOGFILE
2 LOGKEY(A10) /* DATE(YYMMDD), LOGTYPE(A2), PAGENUM(A2)
2 REDEFINE LOGKEY
3 #LOGDATE(A6)
2 #LOGTYPE(A2)
2 LOGTEXT(A79/20)
1 #LOG-TITLE(A30)
1 #LINE(A79/20)
1 #I(N2)
1 #NEXT(A1)
1 #REACHED-END(L)
END-DEFINE
*
READ LOGFILE BY LOGKEY STARTING FROM '1206050101' TO '1206050199'
PERFORM MAP-SCREEN
AT END OF DATA
#REACHED-END := TRUE
PERFORM MAP-SCREEN
END-ENDDATA
END-READ
*
DEFINE SUBROUTINE MAP-SCREEN
DECIDE ON FIRST VALUE OF #LOGTYPE
VALUE '01'
#LOG-TITLE := 'CENTRAL LOG'
VALUE '02'
.... SO ON
END-DECIDE
FOR #I 1 TO 20
MOVE LOGTEXT(#I) TO #LINE(#I)
END-FOR
RESET #NEXT
MAP (AD=O)
1/1 *DAT4U 23T #LOGDATE #LOG-TITLE 70T *PROGRAM //
#LINE(1) / #LINE(2) / #LINE(3) / #LINE(4) / #LINE(5) /
#LINE(6) / #LINE(7) / #LINE(8) / #LINE(9) / #LINE(10) /
#LINE(11) / #LINE(12) / #LINE(13) / #LINE(14) / #LINE(15) /
#LINE(16) / #LINE(17) / #LINE(18) / #LINE(19) / #LINE(20) /
'NEXT PAGE:' *IN #NEXT(AD=U)
*
IF #NEXT = ' '
FETCH 'LOGMENU'
END-IF
*
IF #REACHED-END
REINPUT 'LAST PAGE HAS BEEN DISPLAYED' MARK *#NEXT
END-IF
END-SUBROUTINE
END
Using the out-of-sync method:
DEFINE DATA LOCAL
1 LOGFILE VIEW OF LOGFILE
2 LOGKEY(A10) /* DATE(YYMMDD), LOGTYPE(A2), PAGENUM(A2)
2 REDEFINE LOGKEY
3 #LOGDATE(A6)
2 #LOGTYPE(A2)
2 LOGTEXT(A79/20)
1 #HOLD-LOGTYPE(A2) /* ADDED THIS FIELD FOR OUT-OF-SYNC METHOD
1 #LOG-TITLE(A30)
1 #LINE(A79/20)
1 #I(N2)
1 #NEXT(A1)
1 #REACHED-END(L)
END-DEFINE
*
READ LOGFILE BY LOGKEY STARTING FROM '1206050101' TO '1206050199'
AT START OF DATA
MOVE #LOGTYPE TO #HOLD-LOGTYPE
FOR #I 1 TO 20
MOVE LOGTEXT(#I) TO #LINE(#I)
END-FOR
ACCEPT IF #HOLD-LOGTYPE = 'ZZ'
END-START
*
PERFORM MAP-SCREEN
MOVE #LOGTYPE TO #HOLD-LOGTYPE
FOR #I 1 TO 20
MOVE LOGTEXT(#I) TO #LINE(#I)
END-FOR
*
AT END OF DATA
#REACHED-END := TRUE
PERFORM MAP-SCREEN
END-ENDDATA
END-READ
*
DEFINE SUBROUTINE MAP-SCREEN
DECIDE ON FIRST VALUE OF #HOLD-LOGTYPE
VALUE '01'
#LOG-TITLE := 'CENTRAL LOG'
VALUE '02'
.... SO ON
END-DECIDE
*
RESET #NEXT
MAP (AD=O)
1/1 *DAT4U 23T #LOGDATE #LOG-TITLE 70T *PROGRAM //
#LINE(1) / #LINE(2) / #LINE(3) / #LINE(4) / #LINE(5) /
#LINE(6) / #LINE(7) / #LINE(8) / #LINE(9) / #LINE(10) /
#LINE(11) / #LINE(12) / #LINE(13) / #LINE(14) / #LINE(15) /
#LINE(16) / #LINE(17) / #LINE(18) / #LINE(19) / #LINE(20) /
'NEXT PAGE:' *IN #NEXT(AD=U)
*
IF #NEXT = ' '
FETCH 'LOGMENU'
END-IF
*
IF #REACHED-END
REINPUT 'LAST PAGE HAS BEEN DISPLAYED' MARK *#NEXT
END-IF
END-SUBROUTINE
END
IF #REACHED-END
MOVE 'LAST PAGE HAS BEEN DISPLAYED' TO #MSG
END-IF
MAP (AD=O) WITH TEXT #MSG MARK *#NEXT
1/1 *DAT4U 23T #LOGDATE #LOG-TITLE 70T *PROGRAM //
....
instead of REINPUT you should have a simpler solution in option 1. If you really need the REINPUT for some reason, then the IF #REACHED-END before the MAP statement could SET CONTROL ‘N’ to simulate an enter and step through to your REINPUT.
I would use the READ … TO option together with the END-OF-DATA clause.
As specified in the Natural 4.2.5 manual in respect of the TO option (as opposed to THRU)
When the READ loop terminates because the
end-value has been reached, the view contains the
last record of the specified range.