FOR statement
Starts an iterating block that reads a record from each of one or more tables at the start of each block iteration. Use an END statement to end a FOR block.
Data movement
Block properties
Iteration, record reading, record scoping, frame scoping, transactions by default.
Syntax
EACHFIRST
Uses the criteria in therecord-phrase
to find the first record in the table that meets that criteria. The AVM finds the first record before any sorting.The following statement displays Customer 1 (CustNum
is the primary index of the Customer table), not the Customer with the lowestCreditLimit
:
The following statement displays the Customer with the lowestCreditLimit
:
See the Notes section for more information on using this option.LAST
Uses the criteria in therecord-phrase
to find the last record in the table that meets that criteria. The AVM finds the last record before sorting.
The procedure above displays the Customer with the highest Customer number (CustNum
is the primary index of the Customer table), not the Customer with the highestCreditLimit
.A procedure that displays the Customer with the highestCreditLimit
looks like the following:
See the Notes section for more information on using this option.record-phrase
Identifies the set of records you want to retrieve. This can also be the built-in buffer name,proc-text-buffer
, that you can use to return table rows from a stored procedure.To use FOR EACH/FIRST/LAST to access a record in a table defined for multiple databases, you must qualify the record’s table name with the database name.This is the syntax forrecord-phrase
:
Specifying multiple occurrences ofrecord-phrase
selects the tables using an inner join.For more information onrecord-phrase
and inner joins, see the Record phrase reference entry.query-tuning-phrase
Allows programmatic control over the execution of a DataServer query. Following is the syntax for thequery-tuning-phrase
:
For more information on thequery-tuning-phrase
, refer to the appropriate DataServer Guide (OpenEdge Data Management: DataServer for ODBC, OpenEdge Data Management: DataServer for Oracle, or OpenEdge Data Management: DataServer for Microsoft SQL Server).BREAK
Over a series of block iterations, you might want to do some work based on whether the value of a certain field changes. This field defines a break group. For example, you might be accumulating some value, such as a total. You use the BREAK option to defineState
as the break group. For example:
Here, the AVM accumulates the totalCreditLimit
for all the customers in the Customer table. Each time the value of theState
field changes, the AVM displays a subtotal of theCreditLimit
values for customers in that state.You can use the BREAK option anywhere in the block header, but you must also use the BY option to name a sort field.You can use the BREAK option in conjunction with the ACCUMULATE statement and ACCUM function. For more information, see the reference entries for those language elements.BYexpression
[ DESCENDING ]
Sorts the selected records by the value ofexpression
. If you do not use the BY option, the AVM retrieves records in the order of the index used to satisfy therecord-phrase
criteria, or the primary index if no criteria is given. The DESCENDING option sorts the records in descending order (not in the default ascending order).Note: You cannot reference a BLOB or CLOB field in the BY option.You can use multiple BY options to do multi-level sorting. For example:
Here, the Customers are sorted in order byCreditLimit
. Within eachCreditLimit
value, Customers are sorted alphabetically byName
.There is a performance benefit if an index on expression exists: BREAK BY does not have to perform the sort that is otherwise required to evaluate FIRST, LAST, FIRST-OF, and LAST-OF expressions.COLLATE (string
,strength
[ ,collation
] ) [ DESCENDING ]string
strength
A CHARACTER expression that evaluates to an ABL comparison strength or an International Components for Unicode (ICU) comparison strength.The ABL comparison strengths include:
- RAW — Generates a collation value for the string based on its binary value.
- CASE-SENSITIVE — Generates a case-sensitive collation value for the string based on a particular collation. If you specify this strength with an ICU collation, the AVM applies the ICU TERTIARY strength.
- CASE-INSENSITIVE — Generates a case-insensitive collation value for the string based on a particular collation. If you specify this strength with an ICU collation, the AVM applies the ICU SECONDARY strength.
- CAPS — Generates a collation value for the string based on its binary value after converting any lowercase letters in the string to uppercase letters, based on the settings of the Internal Code Page (
-cpinternal
) and Case Table (-cpcase
) startup parameters.The ICU comparison strengths include:
- PRIMARY — Generates a collation value for the base characters in the string.
- SECONDARY — Generates a collation value for the base characters and any diacritical marks in the string.
- TERTIARY — Generates a case-sensitive collation value for the base characters and any diacritical marks in the string.
- QUATERNARY — Generates a case-sensitive collation value for the base characters and any diacritical marks in the string, and distinguishes words with and without punctuation. ICU uses this strength to distinguish between Hiragana and Katakana when applied with the ICU-JA (Japanese) collation. Otherwise, it is the same as TERTIARY.
Note: Use ICU comparison strengths only with ICU collations.collation
variable
=expression1
TOexpression2
[ BYk
]
Identifies the name of a field or variable whose value you are incrementing in a loop. Theexpression1
is the starting value forvariable
on the first iteration of the loop. Thek
is the amount to add tovariable
after each iteration and must be a constant. It (k
) defaults to 1. Thevariable, expression1
, andexpression2
parameters must be integers.Whenvariable
exceedsexpression2
(or is less thanexpression2
ifk
is negative) the loop ends. Sinceexpression1
is compared toexpression2
at the start of the first iteration of the block, the block can be executed 0 times. The AVM re-evaluatesexpression2
on each iteration of the block.WHILEexpression
Indicates the condition in which you want the FOR EACH block to continue processing the statements within it. Using the WHILEexpression
option causes the block to iterate as long as the condition specified by the expression is TRUE or the AVM reaches the end of the index it is scanning, whichever comes first. The expression is any combination of constants, operators, field names, and variable names that yield a logical value.TRANSACTIONSTOP-AFTERexpression
The STOP-AFTER phrase specifies a time-out value for a DO, FOR, or REPEAT block. The integer expression specifies the number of seconds each iteration of a block has until a time-out occurs. If a time-out occurs, the AVM raises the STOP condition and default STOP condition handling occurs. Use an ON STOP phrase on the block (or an enclosing block) to alter the default STOP condition handling.If the block iteration completes before the specified time expires, the timer resets toexpression
for the next iteration. In other words, the timer is limited to the scope of a single block iteration. If a block with a STOP-AFTER phrase encloses another block or calls another block, the timer continues while the inner blocks execute.If a block with a STOP-AFTER phrase contains a nested block with a STOP-AFTER phrase, then each has a timer in effect. If the outer block timer expires while the inner block is executing, the STOP condition is raised even if the timer for the inner block has not expired.If the STOP condition is handled and execution resumes within the scope of a block with a STOP-AFTER phrase, no timer is in effect until the next iteration of a block with a STOP-AFTER phrase. In other words, all old timers are dismissed but new timers can now be established.When the timer expires, the STOP condition is raised on the current statement.Two important use cases for the STOP-AFTER phrase are to time-limit dynamic queries and to time-limit a procedure call. The following example time-limits a procedure call using a RUN statement:
Use this technique to also make timed calls to class methods and user-defined functions.The following example is simplified code that lets you try different STOP-AFTER cases.
If you run this code as is, the outer DO block establishes a 5 second time limit for the work of the DO block and all inner blocks. When the inner FOR EACH block starts, another timer is established for the first iteration of this block. When the first FOR EACH iteration completes, its timer is reset to 1 second for the next iteration. Meanwhile, the outer timer on the DO block continues without interruption.The FOR EACH block completes and execution continues forward to the REPEAT block, which is an endless loop. The REPEAT block also has a 1 second timer for each iteration of the block. At some point, the outer 5 second timer elapses and the AVM raises the STOP condition. The STOP condition is raised on the statement the AVM was executing when the timer elapsed. Normal STOP handling proceeds from that point.As the stack unwinds during STOP processing, the AVM encounters the ON STOP phrase on the DO block. The ON STOP phrase dismisses the STOP condition and resumes normal execution with the next statement following the DO block, as directed by the LEAVE option.If you remove the comments from the IF statement in the REPEAT block, the block will complete within the outer time limit and the STOP condition is not raised.If you want to experiment with elapsed timers on an inner block, insert a complex operation inside the FOR EACH block.In the following example, the STOP-AFTER expression is modified during program execution:
Because theSTOP-AFTER
expression is re-evaluated for each iteration of a looping block, any changes made to the expression during the iteration effect the timer for the block. In the example, theSTOP-AFTER
time limit is specified by the variablestopTime
, which is initially set to 30 seconds. The procedure contains an iterating block which runs a procedure that executes for 10 seconds.On the first iteration of theDO WHILE TRUE
loop,stopTime
is 30 seconds. The loop executes for 10 seconds, and then dividesstopTime
by 2. On the second iteration, thestopTime
is 15 seconds; again the loop executes for 10 seconds, and then dividesstopTime
by 2. On the third iteration, thestopTime
is 8 seconds. This time, the procedurespinHere
runs for 8 seconds and then raisesSTOP
. TheSTOP
condition is handled by theDO
block, and then the program displays the message program finished.If a code block is called with a time limit of zero, the block is executed as if theSTOP-AFTER
phrase was omitted from the block declaration.Consider the following example:
In this example, procedurefoo
is run from within a timed block with a 10 second time limit; procedure bar is called from within the timed block, and contains an iterating block that specifies theSTOP-AFTER
phrase. Because the value of theSTOP-AFTER
expression evaluates to zero (that is, the current value of thebarLimit
variable), the block withinbar
is executed as an untimed block. However, the rules for execution of an untimed block within a timed block apply, so the untimed block in bar is executed with an implicit iteration time limit of 10 seconds.Other points to consider are:
- If the expression evaluates to zero or less, then this is the equivalent of not specifying a STOP-AFTER phrase.
- STOP-AFTER phrases are not intended to interact with user interfaces.
- Blocking calls to third party software components, where the AVM has transferred execution control, cannot be timed out. This category includes operating system calls, MS Windows system calls, and calls to any third party DLLs and Unix shared objects.
on-error-phrase
Describes the processing that takes place when there is an error during a block. This is the syntax for the ON ERROR phrase:
For more information, see the ON ERROR phrase reference entry.on-endkey-phrase
Describes the processing that takes place when the ENDKEY condition occurs during a block. This is the syntax for the ON ENDKEY phrase:
For more information, see the ON ENDKEY phrase reference entry.on-quit-phrase
Describes the processing that takes place when a QUIT statement is executed during a block. This is the syntax for the ON QUIT phrase:
For more information, see the ON QUIT phrase reference entry.on-stop-phrase
Describes the processing that takes place when the STOP conditions occurs during a block. This is the syntax for the ON STOP phrase:
For more information, see the ON STOP phrase reference entry.frame-phrase
Specifies the overall layout and processing properties of a frame. For more information onframe-phrase
, see the Frame phrase reference entry.for-body
for-logic
catch-block
Specifies a CATCH statement that defines error handling code for one or more error types. For more information oncatch-block
, see the CATCH statement reference entry.finally-block
Specifies a FINALLY statement that defines the processing that must occur after all other processing in the block occurs. For more information onfinally-block
, see the FINALLY statement reference entry.END
Specifies the end of the FOR block definition. You must end the FOR block definition with the END statement.ExamplesThis procedure reads Customer records that have a
CustNum
less than 12, sorting the records in order byState
before displaying them:
The next procedure gets information from four related tables (Customer, Order, OrderLine, and Item) and displays some information from each. Before displaying the information, the FOR EACH statement sorts it in order by the
PromiseDate
field, then, within that field, in order byCustNum
. Within theCustNum
field, the data is sorted by theLineNum
field.
This procedure uses the LAST option to display information on the last Order of each Customer:
Notes
- At compile time, ABL determines which index or indexes to use for retrieving records from a table, based on the conditions in the Record phrase. For compatibility with Progress Version 6 or earlier, you can force ABL to use only one index by specifying the USE-INDEX option or by using the Version 6 Query (-v6q) parameter.
- If you specify the
-v6q
startup parameter, an index component is involved in an equality match if it is used in the Record phrase conditions in the following form:
Where theexpression
is independent of any fields in the table that the index is being selected from. A condition involving OF and USING are equivalent to this form. A field is involved in a range match if it is used in a condition of this form:
The BEGINS operator translates into two range matches for a field.An equality or range match is considered active if the equality or range condition stands on its own or is related to other conditions solely through the AND operator (for example, not through OR or NOT).A field is involved in a sort match if it is used in a BY option of this form:
- If you specify the
-v6q
startup parameter, the following list describes the rules the OpenEdge database manager uses to choose an index for an OpenEdge database:
- If you specify the record by ROWID, the AVM accesses the record directly without using an index.
- If you use the USE-INDEX option, in the
record-phrase
, the AVM uses the index you name in that option.- For each index in the table, the ABL compiler looks at each index component in turn and counts the number of active equality, range, and sort matches. ABL ignores the counts for any components of an index that occur after a component that has no active equality match. ABL compares the results of this count and selects the best index. ABL uses the following order to determine the better of any two indexes:
- If one index is unique and all of its components are involved in active equality matches and the other index is not unique, or if not all of its components are involved in active equality matches, ABL chooses the former of the two.
- Select the index with more active equality matches.
- Select the index with more active range matches.
- Select the index with more active sort matches.
- Select the index that is the primary index.
- Select the first index alphabetically by index name.
- If you specify the
-v6q
startup parameter, the AVM might have to scan all the records in the index to find those meeting the conditions, or it might have to examine only a subset of the records. This latter case is called bracketing the index and results in more efficient access. Having selected an index as previously described, the ABL compiler examines each component as follows to see if the index can be bracketed:
- If the component has an active equality match, ABL can bracket it, and it examines the next component for possible bracketing.
- If the component has an active range match, ABL can bracket it, but it does not examine the remaining components for possible bracketing.
- If the component does not have an active equality match or an active range match, ABL does not examine the remaining components for bracketing.
- If you specify the v6q parameter, any conditions you specify in the
record-phrase
that are not involved in bracketing the selected index are applied to the fields in the record itself to determine if the record meets the overallrecord-phrase
criteria. For example, assume that the f table has fields a, b, and c involved in two indexes:Table 37 shows the index ABL selects and the bracketed part of the index for variousrecord-phrases
.
Table 37: Progress Version 6 index selection examples Record phrase Index selected Bracketing on f WHERE a = 3 AND b = 2 AND c = 3 I1 a + b + c f WHERE a = 3 I1 a f WHERE c = 1 I2 c f WHERE a = 3 AND b > 7 AND c = 3 I1 a + b f WHERE a = 3 AND c = 4 I1 a f WHERE b = 5 I1 None of the fields1 f WHERE a = 1 OR b >5 I1 None of the fields1 f WHERE (a >= a1 AND a <= a2)
OR (a1=0) I1 None of the fields2 f WHERE a >= (IF a1 NE 0 THEN a1
ELSE -99999999) AND
a <= (IF a1 NE 0 THEN a2
ELSE +99999999) I1 a2- The FIRST and LAST keywords are especially useful when you are sorting records in a table in which you want to display information. Often, several related records exist in a related table, but you only want to display the first or last related record from that table in the sort. You can use FIRST or LAST in these cases.
Suppose you were interested in displaying the date when each Customer first placed an order. This procedure displays the Customer number and date of the first Order:
The following procedure displays the last Order Line of every Order, sorted by the Price of the Item and by the Promised Date of the Order:
- If you want the AVM to use a specific index, you must specify the first component of that index in the record phrase of the FOR statement.
- You cannot reference a BLOB or CLOB field in a WHERE clause.
- For more information on the FOR statement, see OpenEdge Getting Started: ABL Essentials.
- For SpeedScript, the
on-endkey-phrase
and theon-quit-phrase
do not apply.See alsoCATCH statement, FINALLY statement, FIND statement, Frame phrase, ON ENDKEY phrase, ON ERROR phrase, ON QUIT phrase, ON STOP phrase, Record phrase
OpenEdge Release 10.2B
|