Tech News Back Issues Issue: 012904

Aggregate Values in Omnis Studio Reports

By David Swain
Polymath Business Systems

Every month or two a question is posted on the Omnis Underground list server regarding "totals" in Omnis Studio reports. The concepts behind and practice of dealing with aggregate values are actually quite simple, but veteran Omnis programmers who have gotten used to the way Omnis 7 handled them sometimes have a difficult time switching to the "new and improved" techniques for Omnis Studio. Let's try to clear this up!

Aggregate Values Defined

For any variable in an Omnis Studio report, we can track five aggregate values. An aggregate value for a variable is one derived as a summary of the individual values that variable contains during the process of building the report. As values pass into and out of that variable for various records, aggregate values can be accumulated in a special buffer. These cumulative values can then be tapped from within a Subtotal or Totals section when the printing of such a section is triggered.

The choices of aggregate value types are: total, count, average, minimum and maximum. Here is a brief description of each:

Total The numeric sum of all non-NULL values held by the variable. The numeric equivalent of each value is used to accumulate this total. A string value is treated as a zero unless it is equivalent to a number.
Count The count of all non-NULL values held by the variable. Empty and zero values are counted, but NULL values are not. The data type of the variable is not important here. The count value returned is numeric.
Average The numeric average of all non-NULL values held by the variable. This is derived by dividing the total by the count. Zero values are included in the average, but NULL values are not. Date values can be averaged, but a numeric result is given. This can be converted back to a date value using the dat() function.
Minimum The numeric minimum of all non-NULL values held by the variable. Date variables can be used to derive a minimum, but a numeric result is given. This can be converted back to a date value using the dat() function.
Maximum The numeric maximum of all non-NULL values held by the variable. Date variables can be used to derive a maximum, but a numeric result is given. This can be converted back to a date value using the dat() function.

Basic Technique

To cause Omnis Studio to track aggregate values for a variable used in a report, we must place a field that represents that variable (one that has the name of that variable as its dataname property value) in a Subtotal or Totals section within the report and set that fields totalmode property to some value other than kTmNone. It is that simple. Setting the totalmode property for a field in the Record section (or in a Page Header or Footer section) has no effect on generating aggregate values for the associated variable in Omnis Studio.

There are five choices for the totalmode property value besides kTmNone (which has a numeric equivalent of 0). Each value is given as a property constant, but each such constant also represents a numeric value (shown here in parentheses) that can be used in its stead. The property constants are: kTmTotal (1), kTmCount (3), kTmAverage (2), kTmMinimum (4) and kTmMaximum (5). When dynamically setting the value of this property, either a property constant or its numeric equivalent can be used...but we won't be doing anything that advanced in this article. Perhaps a simple example is in order.

Simple Count Example

Let's create a report to demonstrate the basic technique for deriving an aggregate value. Suppose we assign a file named "customerFile" as the Main File for this report class. In the Record section we place a field representing the variable customerFile.lastName. In the Totals section we place a field representing the variable customerFile.address2. Both variables are of Character data type.

For the field in the Totals section we will also set the totalmode property value to kTmCount using the dropdown list of possible values provided. The Totals section will then show us how many records used for this report have a non-NULL value for customerFile.address2. This simple layout will look like:

On printing the report to the screen for my test database, the Totals section displays the number 161. This is, in fact, the number of Customer records in my database. If I change the value of customerFile.address2 in one record to be NULL (by calculating it as the value of #NULL), the Totals section displays 160 the next time it is printed (thus demonstrating that NULL values are not included in the count).

This is just one example. The other aggregation types work in a similar way. There are just a few additional things we must know to take advantage of this facility.

Basic Rules

There are a few basic rules as to how this technology works. Understand these and you should have no problems with "totals" in your reports.

First, only a field with a non-zero totalmode property value can turn on aggregate value tracking for its associated variable. If no such field exists for a variable, no aggregate values are accumulated for it.

Second, if tracking of one aggregate value type is switched on for a variable, all five aggregate values are actually tracked. So if we switch on tracking of a count for a variable, the total, average and the others are also accumulated - even if we never do anything with them.

Finally, if aggregate values are tracked at one subtotal level, they are tracked at all levels. We don't need to place a field with a non-zero totalmode value in each subtotal level section for the same variable in order to use that variable's aggregate values in each of those places. Only one is absolutely required. We will see how this can come into play in the next section.

Using Aggregate Values In Expressions

There will be times when we need to access an aggregate value in a way other than displaying it in an Entry field with a specific totalmode setting. For example, we may need to derive other values from one using an expression. Or we may want to display one in a Text object using square bracket notation. In either of these cases, we need to use Omnis Notation to access a specific aggregate value for a given variable.

Since each such variable automatically has all five aggregate values tracked for it, Omnis Studio can't make any assumptions about which of those values to use in a specific place. So we need some means of stipulating this ourselves. Omnis Studio has just the answer...

The creators of Omnis Studio simply extended Omnis Studio Notation with properties for variables that allow us to access their aggregate values in a report. We can use these property names just as we would for working with the properties of a component object on a window. The five aggregate value properties for a variable are: $total, $count, $average, $minimum and $maximum. So to access the count value for our customerFile.address2 variable somewhere in a subtotal or totals section, we would could use the notation customerFile.address2.$count.

The scope of these properties is extremely limited. They are only in scope while the subtotal or totals section prints. Even just before or just after the Do default command line in the $print method for such a section, these properties are out of scope. If we need to bring an aggregate value back into the methods of our report, we must transfer it to another variable out on the report "surface" that can retain the value beyond the "life" of the section. Interestingly, hash variables seem to be the most stable for this purpose. We will examine this in a later article in this series.

Let's look at another, more complex example that illustrates a few more of the principles mentioned above.

Subtotal Example

Let's modify the report we built earlier in this article. First, we will add a Subtotal level 1 section to the report by setting the report's subtotal1 property value to kTrue. Notice that three lines are added to the report class in addition to the section banner. Next we will move the field that was in the Totals section up into the Subtotal level 1 section. Now we have a few changes to make to this field.

We are going to change the dataname property value for the field to customerFile.dateEntered. This is a field of Short date data type that is populated with the current date when each new record is created. For the moment, we will set the totalmode property value of this field back to kTmNone to prove a point...then we will do something more interesting.

In the Totals section we will place five Text objects. (If you use Label objects, this will not work as they do not support square bracket notation.) Into these five objects, we will place the following five text strings:

Count: [customerFile.dateEntered.$count]

Total: [customerFile.dateEntered.$total]

Average: [customerFile.dateEntered.$average]

Minimum: [customerFile.dateEntered.$minimum]

Maximum: [customerFile.dateEntered.$maximum]

The layout for this report class should now look like:

Before we print this, we must also set the sort order for the report. I am setting the sort variables as follows:

So a change in the stateCode value will trigger the printing of the Subtotal level 1 section and the lastName values will be sorted alphabetically within each State. We are now ready to print this report to the screen.

When we print the report, we see a date value at each subtotal break and a lot of empty values following the label headers in the Totals section at the end of the report (except for Count:, which is followed by 0). This is because no aggregate values are being tracked for the customerFile.dateEntered variable. If we set the totalmode property value of the field in the Subtotal level 1 section to kTmCount, we get a much different result.

We now get a count value at each subtotal break. This shows us how many people are listed for each State. Remember that this is the only field that sets any kind of aggregation for customerFile.dateEntered - and it only set up the count. We demonstrated above that the notational aggregation properties do not cause aggregation to be applied to the associated variable.

But now, look what is happening in the Totals section! Here is what it looks like in my report:

The $count property returns the number of records involved in the report (none of the dateEntered values were NULL). The $total property shows the sum of the numeric equivalents of each date value involved in the report. This is a very large number - and not terribly meaningful for a date variable. But the $average, $minimum and $maximum values show more promise. Even though they are also large, they can be converted back into date values using the dat() function. If we change the text value of these Text objects as follows, we get the result below:

Average: [dat(customerFile.dateEntered.$average)]

Minimum: [dat(customerFile.dateEntered.$minimum)]

Maximum: [dat(customerFile.dateEntered.$maximum)]

We can apparently derive information about the range of dates encountered during the creation of this report. Perhaps you can conceive of a few possible uses for this...

So What Did We Learn From All This?

First, we learned how Omnis Studio expects us to set up report fields for generating aggregate values. We learned the five types of aggregation Omnis Studio offers to us, that all five are accumulated even if we only want one of them and how we can access these values if we need to do further processing with them. And we learned that there is a "scope" for these aggregate values that is quite narrow (although no examples have been given yet where we attempt to carry those values outside that scope).

Next Time

So far we have mainly talked about aggregate values in the Totals section that prints at the end of a report. We have not discussed in any detail how subtotal sections are set up and triggered. That will be the subject for the next article.

© 2004 Copyright of the text and images herein remains with the respective author. No part of this newsletter may be reproduced, transmitted, stored in a retrieval system or translated into any language in any form by any means without the written permission of the author or Raining Data.
Omnis® and Omnis Studio® are registered trademarks, and Omnis 7™ is a trademark of Raining Data UK Ltd. Other products mentioned are trademarks or registered trademarks of their corporations. All rights reserved.