Try OpenEdge Now
skip to main content
ABL Essentials
Procedure Blocks and Data Access : Language statements that define blocks : Data access without looping: the FIND statement : Using the CAN-FIND function
 
Using the CAN-FIND function
Often you need to verify the existence of a record without retrieving it for display or update. For example, your logic might need to identify each Customer that has at least one Order, but you might not care about retrieving any actual Orders. To do this, you can use an alternative to the FIND statement that is more efficient because it only checks index entries wherever possible to determine whether a record exists, without going to the extra work of retrieving the record itself. This alternative is the CAN-FIND built-in function. CAN-FIND takes a single parameter, which can be any record selection phrase. The CAN-FIND function returns TRUE or FALSE depending on whether the record selection phrase identifies exactly one record in the database.
For example, imagine that you want to identify all Customers that placed Orders as early as 1997. You don't need to retrieve or display the Orders themselves, you just need to know which Customers satisfy this selection criterion. The following simple procedure accomplishes this:
FOR EACH Customer NO-LOCK WHERE Customer.Country = "USA":
  IF CAN-FIND(FIRST Order OF Customer WHERE Order.OrderDate < 1/1/98) THEN
    DISPLAY Customer.CustNum Customer.Name.
  ELSE
    DISPLAY Customer.CustNum "No 1997 Orders" @ Customer.Name.
END.
This procedure uses a little display trick you haven't seen before. If the Customer has any Orders for 1997, then the procedure displays the Customer name. Otherwise, it displays the text phrase No 1997Orders. If you include that literal value in the DISPLAY statement, it displays in its own column as if it were a field or a variable. To display it in place of the Name field, use the at-sign symbol (@). The following figure shows the result.
Figure 23. Result of CAN-FIND function procedure
The CAN-FIND function takes the argument FIRST Order OF Customer WHERE OrderData < 1/1/98. Why is the FIRST keyword necessary? The CAN-FIND function returns TRUE only if exactly one record satisfies the selection criteria. If there's more than one match, then it returns FALSE—without error—just as it would if there was no match at all. For example, if you remove the FIRST keyword from the example procedure and change the literal text to be No unique 1997 Order, and rerun it, then you see that most Customers have more than one Order placed in 1997:
FOR EACH Customer NO-LOCK WHERE Customer.Country = "USA":
  IF CAN-FIND (Order OF Customer WHERE Order.OrderDate < 1/1/98) THEN
    DISPLAY Customer.CustNum Customer.Name.
  ELSE
    DISPLAY Customer.CustNum "No unique 1997 Order" @ Customer.Name.
END.
After you page through the results, you see just a few records that don't satisfy the criteria, as shown in the following figure.
Figure 24. Result of CAN-FIND function procedure without FIRST keyword
Because you don't get an error if there's more than one match, it's especially important to remember to define your selection criteria so that they identify exactly one record when you want the function to return TRUE.
The CAN-FIND function is more efficient than the FIND statement because it does not actually retrieve the database record. If the selection criteria can be satisfied just by looking at values in an index, then it doesn't look at the field values in the database at all. However, this means that the record referenced in the CAN-FIND statement is not available to your procedure. For example, this variation on the example tries to display the OrderDate from the Order record as well as the Customer fields:
FOR EACH Customer NO-LOCK WHERE Customer.Country = "USA":
  IF CAN-FIND(FIRST Order OF Customer WHERE Order.OrderDate < 1/1/98) THEN
    DISPLAY Customer.CustNum Customer.Name Order.OrderDate.
  ELSE
    DISPLAY Customer.CustNum "No 1997 Orders" @ Customer.Name.
END.
This results in the error shown in the following figure, because the Order record is not available following the CAN-FIND reference to it.
Figure 25. CAN-FIND error message
If you need the Order record itself then you must use a form that returns it to you:
FOR EACH Customer NO-LOCK WHERE Customer.Country = "USA":
  FIND FIRST Order OF Customer NO-LOCK WHERE OrderDate < 1/1/98 NO-ERROR.
  IF AVAILABLE Order THEN
    DISPLAY Customer.CustNum Customer.Name Order.OrderDate.
  ELSE
    DISPLAY "No 1997 Orders" @ Customer.Name.
END.
When you run this code, you see the OrderDate as well as the Customer fields except in those cases where there is no Order from 1997, as shown in the following figure.
Figure 26. FIND FIRST Order result
The samples so far have shown the CAN-FIND function in an IF-THEN statement. You can also use it anywhere where a logical (TRUE/FALSE) expression is valid in a WHERE clause, such as this:
FOR EACH Customer NO-LOCK WHERE Customer.Country = "USA" AND
  CAN-FIND(FIRST Order OF Customer WHERE Order.OrderDate < 1/1/98):
  DISPLAY Customer.CustNum Customer.Name.
END.
The next chapter continues the discussion on building complex procedures, with details on record buffers and record scope.