Query

<< Click to Display Table of Contents >>

Navigation:  Apollo VCL Components > Apollo VCL Component Reference > TApolloTable > TApolloTable Methods >

Query

Declaration

function Query( sExpression ): LongInt;

Description

Sets an ultra high speed filter. Query uses open indexes to determine whether a filter can be constructed based upon index keys rather than the evaluation of data extracted from the table.

 

The query optimization algorithms used may result in filter setting and data extraction hundreds of times faster than standard xBase filters. If query optimization cannot be performed, a standard filter is set (see SetFilter).

 

An Query filter respects current filter settings and the setting of SetDeleted.

 

image\tip.gif For remote tables accessed via the Apollo Database Server, the TApolloQuery component's SQL property supports the use of SELECT statements for retrieving result sets that can be navigated much quicker than TApolloTable.Query.

Parameters

sExpression: A standard xBase expression that defines the query filter conditions. If Query determines that the expression contains field references that match fields used to construct current open indexes, query optimization is performed.

For query optimization to work, a field referenced in the conditional expression must be the first field referenced in an index expression (i.e., the leftmost field if there is more than one).

For example, the benchmark test for Query used the expression below to extract a 3 record subset from a 10,000 record table in less than one second.

 

lRec := ApTbl.Query('STATE="CA" .and. (CITY="San" .or. CITY="Los")'); 

 

The table had indexes open on the fields CITY and STATE.

If xBase functions qualify a field in the index constructing expression, they should also be used in the query expression. For example, if the index expression is 'upper(country)', then the query expression should be in the form 'upper(country) = "GERMANY"'. If the query expression was passed as 'country = "GERMANY"' without the upper conversion function, the query would be set up properly and return the correct results, but it would NOT evaluate a record addition or change correctly if the user entered 'Germany' as the country. New records and edited records use the table data rather than the index key to determine whether or not the record is added to the query subset.

An index used in query optimization does not have to be the controlling index and the query conditional expression may use fields that reference more than one index (as in the STATE, CITY example above).

Query sets a standard filter if it cannot use open indexes to evaluate the passed condition(s). Query may use a mix of query optimization and standard filters (e.g., 'STATE = "CA" .and. AGE > 25' where AGE is not indexed).

Conditional expressions that contain references to aliased fields in tables linked to the current work area via SetRelation are not optimized.

Result

If a query is successful, the record pointer is positioned to the first record in the subset and the record number is returned. If the query is not successful, zero is returned.

Speed Tips

The fastest queries are built using

an equal condition (= or ==)

a less than (<) or less than or equal condition (<=)

a greater than (>) or greater than or equal to condition (>=)

(.NOT. field = condition) is much faster than (field <> condition)

Clearing a Query

To clear a query, pass the query expression as a NULL string ('' or #0). Clearing a query also clears filters set with SetFilter.

Constructing Query Expressions

If using values contained in variables to construct a query expression, the values must become part of the xBase expression string. For example, if a state code is solicited from the user and stored in a variable named 'statecode', the query expression would be constructed as follows:

 

 lRec := ApTbl.Query( 'state = "' + statecode + '"' );

Partial and Exact Matching

If searching for all customers with a first name of "Sam" you might use the following query expression:

 

 'FIRST = "Sam"'

 

However, this may also return records where the first name is Samantha, Samuel, Sampson, or any other name the has the same three first letters as the query value. This is valuable for searching for a partial match. But, if you are wanting to find only those records where the FIRST name field is exactly "Sam" and not just names that start with Sam, you must pad the search string to the width of the field, like this:

 

 FIRST = PadR( "Sam", 20, " " )

 

Or, when the search value is stored in a variable (sVal):

 

 FIRST = PadR( "' + sVal + '", 20, " " )

 

The PadR function is one of the supported xBase functions built in to Apollo's xBase Expression Engine for use within query and index expression like this.

Logical Operator Precedence

Logical operator precedence is .NOT., .AND., and finally .OR. Query expressions with more than two elements using different logical operators to join the elements must be carefully constructed by observing the precedence rules.

 

For example, suppose we want a strange query subset that includes all persons whose last name begins with "B" or everyone who is married. These people must live in a city that begins with the letter 'C'.

 'upper(last) = "B" .or. married .and. city = "C"'

 

This will give us the wrong answer. Because .AND. has a higher precedence than .OR., we will first get all married people who live in a city that begins with 'C'. Once this bitmap has been built, all the persons whose last names begin with 'B' are added to the subset - whether or not they live in a city that begins with 'C'. The correct form of the expression is

 '(upper(last) = "B" .or. married) .and. city = "C"'

 

This changes the evaluation order and forces the first two bitmaps to be evaluated first with the .OR. operator.

Logical Negation

In Boolean terms, .NOT. has the highest precedence. Apollo adheres strictly to this rule. An expression that is given as

 

 'dtos(eventdate) = dtos(date) .and. .not. status = "H"'

(which is perfectly valid in most xBase dialects) will be evaluated as

 

 '.not. (dtos(eventdate) = dtos(date) .and. status = "H")'

which is not what is intended. If using .NOT., always parenthesize each expression in the query to ensure that evaluation proceeds according to intention. The expression above should be passed as

 '(dtos(eventdate) = dtos(date)) .and. (.not. status = "H")'

 

In the first case, the pcode generated is of the form

 field1;value1;=: field2;value2;=: &!

which creates a query bitmap for expression 1, another query bitmap for expression 2, then ands the two bitmaps and finally negates them. This is not what we want. By placing the .not. within the parenthetical expression surrounding the second condition, Apollo generates pcode of the form

 

 field1;value1;=: field2;value2;=: !&

which creates a query bitmap for expression 1, another query bitmap for expression 2, negates the second bitmap, and then ands the two bitmaps. This is what is intended.

Index Types

Query expression equations must be posed in terms of the data type of the index.

1. Character types require a string enclosed in double quotation marks.

   upper(lastname) = "SMITH"

2. Date types require a CTOD function. The date string must be formatted according to the settings of SetDateFormat and SetCentury.

  hiredate > ctod("05/15/94")

3. Logical types simply use the field name.

 married

4. Numeric types require a number (with no quotes).

 age > 25

QKeyVal

QKeyVal is a special xBase function in TApolloTable designed for use with query optimization.

When building query expressions it is sometimes necessary to

(1) refer to the value of the key directly;

(2) target a specific index for optimization.

 

QKeyVal("tagname") allows us to do these things.

 

For example, suppose we had an index that was constructed with SUBSTR(DTOS(startdate),5), which indexes on the month and day of an xBase date. Now, if we attempt to create a query bitmap with this index using

 

ApTbl.Query(SUBSTR('DTOS(startdate,5)) > "0621"')

 

we will get erroneous results (usually zero records) even though QueryTest returns FULL optimization possible..

 

"Why?" you ask. When the query engine attempts to optimize an expression, it extracts the first field name it encounters in the expression and checks each index expression to see if it is also the first field referenced in the key construct. If so, when it comes time to build the bitmap, it replaces the field reference in the expression with the key value. Since the Apollo query optimizer allows the construction of complex xBase expressions on either side of an equation, it leaves the expression surrounding the field reference intact and executes it.

 

The result? If field "startdate" in the example above contained "19940630" then the key for that record would be "0630". The query engine would execute the expression as

 

SUBSTR(DTOS("0630", 5))

 

which would return a NULL value and no records would satisfy the condition. We can't simply say 'startdate > "0621"' because a date type cannot be compared to a string when the expression is tested for correct syntax.

 

We can properly execute the query by using a reference to the actual key value here instead of the expression used to build the index.

 

ApTbl.Query('QKeyVal("startdate") > "0621"')

 

When using QKeyVal it is necessary to specify the tag name of the index because there is no "QKeyVal" expression allowed in an index expression so there is no way to check if the query can be optimized. This also provides a sideline benefit in that we can now target a specific index to be used for optimization. This is valuable if a conditional index exists in the current index list that has the same key expression as a master index. We don't know which one the query engine will use (and the results will be different depending on the index used to build the query bitmap).

 

We can use this to advantage if we wish to construct a bitmap from a non-optimizable condition and then switch index orders. Why would we want to do this? For speed. A non-optimized query sets a standard filter. A Query bitmap can be hundreds of times faster than a standard filter in defining and traversing the required subset.

 

For example, suppose we wish to create a bitmap for all persons in California and there is no index on state (but there are indexes on last name and company).

 // create a temporary conditional index

ApTbl.Index( 'sxtemp.idx', 'last', IDX_NONE, False, 'state = "CA"');

 

// create a bitmap using this index with a condition that satisfies all

// keys in the conditional index (in this case no last name starts with "1")

lRec := ApTbl.Query('QKeyVal("sxtemp.idx") <> "1"');

 

// remove the conditional index

ApTbl.IndexClose;

DeleteFile( 'sxtemp.idx' );

 

// now we can view the subset in the order of any existing index

iPrev := ApTbl.SetOrder( ApTbl.TagArea('company'));

 

image\tip.gif NOTE: When constructing the query bitmap with an eye towards including all of the records in the conditional index, use a NOT EQUAL (<>) condition which will be True for every key in the index. Do not use a .NOT. operator because this inverts the entire bitmap and will include records not found in the conditional index subset.

QKeyVal Rules

1. The tag name passed as a parameter to QKeyVal is the index name with extension (but not with path) in the case of NTX or IDX files. With CDX and NSX indexes, it is the tag name passed when the tag was created.

2. QKeyVal only extracts character type keys. Numeric, logical, and date keys cannot be extracted with QKeyVal.

See Also

DBFilter, FilterDlg, QueryRecCount, QueryTest, SetDeleted, SetFilter, xBase Expression Engine, xBase Functions Supported