logo
Powered by QM on a Rpi server
Home
About OpenQM
Sales and Downloads
Help and Support
About
Login

KnowledgeBase 00094: Dictionary I-Type Records

Last updated: 17 Feb 2017
Applies to: All versions
Search  
Top level index       Full Index Search Tips
Previous article     Next article

This article was originally published as a Tip of the Week.

Introduction

Developers migrating applications to QM from Pick style systems are frequently confused by dictionary I-type records. Actually, these are very simple and offer many features that are not possible with correlatives in A/S-type records.


Format of an I-Type Record

An I-type dictionary record has the following structure:

   1: I {description} 
   2: Expression 
   3: {Conversion code} 
   4: {Column heading} 
   5: Format code 
   6: Single/multivalue flag (S or M) 
   7: {Association name} 
where elements enclosed in curly brackets are optional. Much of this is very similar to an A or S-type record but in a different order. The expression serves broadly the same purpose as a correlative but is much more powerful.


The I-Type Expression

The expression in an I-type dictionary is essentially the same as the right hand side of a QMBasic assignment statement. Unlike correlatives, I-type expressions are not limited to integer arithmetic. With a very few exceptions, all QMBasic functions are available in I-types. So, a simple expression to calculate the due date for an invoice as 30 days after its issue might be

   ISSUE.DATE + 30 
In an I-type expression, names that in QMBasic would be variables relate to other A/C/I/S type items in the same dictionary. Note that, unlike correlatives, numeric constants are not quoted. If there is a need to refer to a field by field number, the data can be extracted from @RECORD using the normal field operators:
   @RECORD<6> + 30 
The above examples show why using D/A/S-type items to name all fields leads to easier maintenance by making it clear to anyone reading the expression what data it references.

Of course, I-type expressions may be much more complex than this simple example.

A common use of I-type expressions is to fetch data from another file using the TRANS() function. This is essentially the same in purpose as a T conversion. For example:

   TRANS(CUSTOMERS, CUST.ID, NAME, 'C') 
to fetch the NAME field of the CUSTOMERS record for which the record id is in the CUST.ID field of the record being processed. Unlike many other multivalue products that support I-type records, QM allows the item being fetched (in this case NAME) to be a further I-type or other calculated value in the remote file's dictionary.

There is an important action of the TRANS() function that developers need to be aware of when retrieving multivalued data. Mark characters in the data returned are demoted by one level (e.g. value marks become subvalue marks). This is done because the TRANS() function can take a multivalued list of record ids in its second argument and returns a composite result with the data for each record appearing as a value in the returned data. There is a similar RTRANS() function that does not demote mark characters.


Multivalued Data

In Pick style systems, a correlative expression used with multivalued data is evaluated once for each value. An I-type, on the other hand, is evaluated just once, processing then entire multivalued data in one operation. To support this, the QMBasic language has many multivalued functions that operate on each field, value or subvalue in turn, returning a similarly structured result. There is a link below to a KnowledgeBase article that describes these.


Compound I-Types

Sometimes it is easier or more efficient to evaluate an I-type expression in stages. This ability is provided by a "compound I-type" which is simply a series of expressions separated by semicolons

The result of evaluating the first expression is stored in an internal variable named @1, the second expression in @2, and so on. Any expression can use the results of earlier expressions. Using just @ as a data name refers to the value of the immediately preceding expression. The value of the whole I-type expression is the value of the final expression.

For example:
   OCONV(DATE(),'DYMD');OCONV(DOB,'DYMD');@1[1,4]-@2[1,4];IF @1[6,5]>@2[6,5] THEN @ ELSE @ - 1 
The above expression, somewhat horrific at first sight, calculates someone's age in whole years based on a date of birth in field DOB. There are four expressions. The first two convert the current date and date of birth to YYYYMMDD format. The third expression calculates the difference in years based on the YYYY portion only. The final expression adjusts this value depending on whether the person has had a birthday in the current year. Other commonly used techniques such as dividing the date difference by 365.25 do not work for people born on 29 February or when performing the calculation on that date.

Unlike many other multivalue products that support I-type records, QM allows compound I-types to be nested to any depth.


Compiling I-Type Records

An I-type is just a QMBasic expression and, like the full QMBasic programming language, it must be compiled before it can be used. The object code is stored in fields 16 onwards of the dictionary record. The ED and SED editors hide this object code as it is binary data which could cause unwanted effects when sent to a terminal device.

The query processor will automatically compile an I-type expression if needed, however, there is an important consideration when using nested I-types. For example, if we have an I-type named PROFIT to calculate the profit we make selling an item

   SELLING.PRICE - COST.PRICE 
and a second I-type named PCT.PROFIT to calculate this as as percentage of the cost price
   PROFIT / COST.PRICE * 100 
the process of compiling the second I-type substitutes the PROFIT expression such that the actual expression compiled is
   (SELLING.PRICE - COST.PRICE) / COST.PRICE * 100 
If we were to execute a query that used the PCT.PROFIT item, the query processor would compile it if it has not been compiled since it was last changed.

Now consider what happens if we had accidentally entered the PROFIT expression as

   COST.PRICE - SELLING.PRICE 
We would notice the error when we run the report using PCT.PROFIT. We could then correct the definition of PROFIT and repeat the query, however, we would get the same incorrect result.

Why does this happen? Because nested I-types are resolved at compile time rather than as a run time subroutine call, changing the PROFIT item does not have any way to automatically invalidate or recompile the PCT.PROFIT item. We need to force recompilation using the COMPILE.DICT (short form CD) command. As a general rule, it is a good idea to compile a dictionary whenever a change is made to an I-type that might be used by another expression.


Related Articles

00044: Multivalue Functions



Please tell us if this article was helpful
Very     Slightly     Not at all
Comments
Email (optional)