Define efficient queries and FOR EACH statements
- Last Updated: January 16, 2024
- 5 minute read
- OpenEdge
- Version 12.8
- Documentation
This section provides some tips for defining queries and FOR EACH
statements.
Using field lists
FOR EACH block. This is the syntax for the
DEFINE QUERY field list:
|
FOR EACH field list:
|
If you specify a list of FIELDS for a buffer, only those fields are
retrieved. If you specify an EXCEPT list, only those fields are not
retrieved.
- The field list option was primarily designed for use with OpenEdge DataServers, which provide a connection to non-OpenEdge databases, such as Oracle and SQL Server. These kinds of databases typically have fixed-length data values that can be much larger than their OpenEdge counterparts, where all data is stored in an efficient, variable-length form.
- The field list has an effect only in a client/server environment, where your client application session has a direct connection to a database server on another system. This is not the recommended architecture for any new applications. A truly distributed application uses an application server to run an OpenEdge session that accesses the database and returns data to the client using temp-tables, as has been extensively discussed in this book. In this environment, you are completely in control of what fields you pass between client and server through your temp-table definitions. The field list mechanism plays no role in this.
- Even if you have a database connection in a client/server environment, the
AVM always retrieves the entire record if you lock the record with an
EXCLUSIVE-LOCK. - The AVM retrieves additional fields beyond those in the field list for its
own purposes, including evaluating some of the selection criteria of the
query or
FOR EACH. - You must remember that the field list is not the same as a display list for
a browse or a field list for the fields in a frame. You define the
DISPLAYlist of columns in a browse independently of the query that the browse uses. If you inadvertently leave out a field in theFIELDSlist of a query definition that is needed by any part of the application that uses the query, your application will generate an error at run time. This can cause serious maintenance problems if your query definitions must explicitly name every field that is used from that query anywhere on the client.
The bottom line here is that in a distributed application, you control the field list
through the definition of temp-tables that pass data from server to client, and the
FIELDS phrase on a query definition is not needed as part of
that definition.
Structure your selection criteria in a join
When you need to retrieve data from multiple joined tables in a single query or
FOR EACH statement, it is important to put the tables into the
proper sequence and to specify your selection criteria as early in the retrieval
process as possible.
OpenEdge does not optimize complex joins in the same way that some other database managers do, rearranging the order of tables and fields. There is a very good reason for this. Because ABL is designed to make it easy and effective to deal with individual records and multiple levels of selection, rearranging a join in a single statement is not typically an issue. For example, this kind of nesting of data retrieval blocks is very typical in ABL business logic:
|
In this kind of code, the developer understands the order in which data is retrieved and is relying on that order to structure the business logic for related tables.
If you join tables in a single statement, the AVM retrieves the data in the order you
specify. The AVM does not second-guess your selection and rearrange the retrieval
for you. This means that you have to take responsibility for structuring your
selection efficiently. For example, if you want to retrieve orders processed today
for Customers with a CreditLimit, this kind of
statement is very inefficient in ABL:
|
Customers have a CreditLimit of 0, so
the Customer selection is going to return nearly all
Customers. On the other hand, only a few
Customers have placed Orders today. It would
be much more efficient to identify the Orders first, and then get
the Customer for each of those Orders:
|
|
- Retrieves each
Customerin theCustomertable in turn, into theCustomerbuffer, regardless of itsCreditLimitor anything else. - Retrieves each
Orderfor eachCustomerin turn, into theOrderbuffer. - Examines the
CreditLimitvalue in theCustomerbuffer to see if it equals 0. - If the
CreditLimitdoes not equal zero, examines theOrderDatein theOrderbuffer to see if it is equal to today’s date.
This is clearly a very inefficient way to go through the data, especially because
there is an index on the OrderDate field and another index on the
CustNum field in both the Order table and the
Customer table that allows the AVM to identify those
Orders and their Customers immediately.
If you are used to working with other data retrieval languages, you might miss the
optimization of complex queries that they do, but ABL gives you control over your
application behavior by presenting you with the data you ask for in the way that you
ask for it. When you are writing real business logic this is much more useful than
having the DBMS evaluate some complex set of expressions on a WHERE
clause that joins multiple tables and return a single processed result.