determining last record found in READ statement

I want to let the user know when the last record has been displayed in a READ statement.

This is what I have, but I have the feeling that I’m doubling my efforts with both a FIND NUMBER and a READ statement.


FIND NUMBER LOG-FILE WITH LOGKEY = #KEY THRU #END-KEY
  #NUM-FOUND := *NUMBER                                  
READ LOG-FILE WITH LOGKEY = #KEY TO #END-KEY
  IF *COUNTER = #NUM-FOUND                               
    #REACHED-END := TRUE                                 
  END-IF
  FOR #I 1 TO 20                                                 
    IF LOGTEXT(#I) NE ' '                                        
      MOVE LOGTEXT(#I) TO #LINE(#I)                              
    END-IF                                                       
  END-FOR                                                        
*                                                                
  RESET #NEXT                                                    
  MAP (AD=O)                                                     
    1/1 *DAT4U 23T #REQ-DATE(EM=MM/DD/YYYY AD=I) #LOG-TITLE(AD=I) 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-READ

Is it possible to do this just using a READ statement?

Hi Dan.

How about

READ LOG-FILE WITH LOGKEY = #KEY TO #END-KEY
at end of data
[...]
end-enddata

Here is a piece of code with discussion below

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.

steve

Hi Matthias;

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.

steve

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 you use something like


  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.