Chapter 11. Advanced Calculations

In the previous two chapters, you’ve learned loads of ways calculations can make your databases work harder for you. But the functions you’ve learned so far can’t make your databases work much smarter. For example, the TextColor function can display the balance due on an invoice in red. But suppose you want that to happen only if the invoice is past due? You could do it in any number of ways, from sorting invoice records to creating a special “past due balance” field. But they all either make extra work for the person using the database, or give you as designer yet another field to worry about every time you tweak, troubleshoot, or repurpose the database. The most elegant solution is to let the database figure out for itself when an invoice is past due. In other words, you want to create a calculation that makes a decision based on current data, whatever it may be. This chapter shows you how to give your calculations that brainpower by using logical functions and other advanced techniques.

Note

Before you read on, now’s a good time to download a fresh copy of the working database. This chapter refers to some layouts and fields that your copy may not have. Head over to MissingManuals.com, as discussed on Section 3.4.5.

Stored, Unstored, and Global Fields

When you defined calculation fields in Chapters 9 and 10, you may have wondered about the Specify Calculation dialog box’s Storage Options button (Figure 11-1). You learned about indexing and other storage options back in Chapter 3 (Section 3.3.4.2). When you use similar options in your calculations, you exercise more control over the information your functions work with.

When you click the Storage Options button in the Specify Calculation dialog box, you can set global storage and indexing options, just like any other field type. You also get a choice you haven’t seen before: “Do not store calculation results.” This exciting new option gives you control over when FileMaker recalculates the value in your formula. With this option, you can make your calculations either save information for later or use the most up-to-date information as your database changes.
Figure 11-1. When you click the Storage Options button in the Specify Calculation dialog box, you can set global storage and indexing options, just like any other field type. You also get a choice you haven’t seen before: “Do not store calculation results.” This exciting new option gives you control over when FileMaker recalculates the value in your formula. With this option, you can make your calculations either save information for later or use the most up-to-date information as your database changes.

Stored and Unstored Calculation Fields

Normally, a calculation field holds a value just like any other field. You can’t edit the value, because FileMaker creates it for you from the calculation. When you use this field on a layout, for example, FileMaker simply displays its results. Depending on whether you turn on the “Do not store calculation result” option in Figure 11-1, FileMaker either grabs saved (stored) results from a table or calculates the value on the fly whenever necessary. For example, when you have an unstored calculation field on a layout, FileMaker recalculates the value as soon as you switch to the layout. In fact, whenever an unstored field is showing onscreen, FileMaker updates it automatically any time something causes its value to change.

At the same time, whenever the data in a field changes, FileMaker also works behind the scenes, finding all the stored calculation fields that depend on the changed field and recalculates them, storing the new value in the field. Whether it’s stored or unstored, a calculation field usually changes because a field used in the calculation has changed, as you’ll see next. Understanding when fields recalculate and how dependencies work can help you avoid future mistakes.

Note

When you use a field in a calculation, you can say the calculation depends on the field (or in other words, that it has a dependency on the field).

Field dependencies

Take a look at the example in Figure 11-2 to see how FileMaker knows when to recalculate fields. It’s very common for calculation fields to use other calculation fields in complex arrangements, as this hierarchy of field dependencies illustrates.

First Name and Last Name are the only editable fields in Figure 11-2. When someone edits one of these fields, FileMaker looks at the field dependencies to see if it needs to do any extra work. The program sees that it needs to recalculate Full Name and then store that Full Name value, which in turn triggers more work. FileMaker must recalculate and store the Full Address value as well. In a sense, that one change trickles down through all the dependent fields as FileMaker calculates and stores, calculates and stores. All this recalculating happens as soon as the user exits the First Name field after making the change.

This picture shows a series of interdependent fields. The Full Name field is a calculation field that uses First Name and Last Name. The Full Address field uses Full Name (and presumably some others). The fields in gray are unstored calculation fields. Collection Letter uses Account Balance and Full Address. Account Balance in turn uses Balance Due. Since an account can have several invoices, each with a balance due, Account Balance actually uses several balance due values—one from each related invoice.
Figure 11-2. This picture shows a series of interdependent fields. The Full Name field is a calculation field that uses First Name and Last Name. The Full Address field uses Full Name (and presumably some others). The fields in gray are unstored calculation fields. Collection Letter uses Account Balance and Full Address. Account Balance in turn uses Balance Due. Since an account can have several invoices, each with a balance due, Account Balance actually uses several balance due values—one from each related invoice.

By contrast, since Collection Letter is an unstored field, FileMaker doesn’t recalculate it right away. (If it did, it would have no place to store the result.) Instead, the program waits until someone brings up the field onscreen, then it runs the calculation on the current data and displays the result.

Again, more things change. To calculate the Collection Letter value, FileMaker grabs the stored value for the Full Address field. But it also needs the Account Balance, which is not stored. As such, it has to first calculate that field. Doing so requires calculating the Balance Due on each invoice in turn, then adding them up to get an Account Balance. Finally, it has the values it needs to show you the Collection Letter. Thus, when you use unstored calculations, FileMaker pulls the data it needs down through the hierarchy of dependencies on an as-needed basis.

Deciding when to store

When you first create a calculation field, FileMaker makes it a stored field automatically, if possible. Some field values aren’t eligible for storage. These are the situations when a calculation field must be unstored:

  • If it depends on any other unstored fields.

  • If it depends on any global fields.

  • If it depends on any related fields.

  • If it depends on any summary fields.

If your calculation meets any of these criteria, FileMaker automatically turns on the “Do not store calculation results–recalculate when needed” option on for you, and it doesn’t let you turn it off. Otherwise, FileMaker automatically stores the field.

  • An unstored field has to be recalculated every time it appears onscreen, in a layout, or in a report. All that recalculation can slow your database down, especially if the unstored field is part of a summary field or a calculation that aggregates many records. So it’s best to store a field unless you need and expect a freshly calculated value every time.

  • If you perform a find based on an unstored calculation field, FileMaker has to go through all your records one-by-one, calculating each one as it goes. The result is a slow search process. If you plan on searching a field, store it. (For more detail, see the box on Section 11.1.2.)

Even if FileMaker can store a certain value, you might not always want it to. Here are some reasons you might turn on that “Do not store” box:

  • Stored fields automatically recalculate as needed when other fields change. But FileMaker has no such automatic behavior for other kinds of information. For example, when you use the Get(CurrentDate) function in a calculation, FileMaker doesn’t recalculate it when the date changes. In general, when you use any of the Get functions (Section 9.2.4), you usually want to make your field unstored to “get” the most up-to-date information.

  • A stored field takes up space on disk, while an unstored field doesn’t. FileMaker 8 files can hold 8 terrabytes of data, so space isn’t a major consideration for most people. But if you’re into slim and trim files, you can save space by making calculations unstored.

  • Lots of stored calculation fields can really slow down record creation. That’s usually not a big deal, but if you often import data (Chapter 17) or use a script to regularly create lots of records, you can speed things up by reducing the number of stored calculations.

Tip

Obviously, there’s some gray area here. When in doubt, store the field. You can always make it unstored later. Choose File → Define Database, then select your field from the list and click Options, and then Storage Options to find the “Do not store calculation results–recalculate as needed” option.

Note

With the exception of global fields and unstored fields, you can set indexing for calculation fields just like any other field, as discussed on Section 3.3.4.2. The available options and their effects depend on the result type of the calculation—text, number, date, or time.

Global Calculation Fields

A calculation field can use global storage (Section 3.3.4.2) just like any other field. When you’re in the Storage Options window and you turn on “Use global storage,” FileMaker calculates just one value for the entire table, rather than a value for each record. If your calculation uses other global fields—and no other fields—then it works just as you expect. That is, when you modify one of the global fields it depends on, FileMaker automatically recalculates its value.

If the calculation uses non-global fields, on the other hand, things get a little tricky. Whenever you change one of the fields referenced in the calculation, FileMaker recalculates the global calculation field using the values from the current record. For example, if you turn on “Use global storage” for your Full Name field, it shows the name of the person you’re looking at when you dismiss the Define Database window. If you were on the first record, that’s whose name you see in the field, just as expected. But as you flip through the records, you see that first user’s name on every record. You’re changing records, but with global storage, the Full Name value stays the same. That’s because nothing it depends on has changed.

Now imagine you switch to the last record. If you then change the First Name field, Full Name recalculates. This new value displays the first and last name from the last record, since FileMaker reevaluates the calculation in its entirety.

This behavior may seem kind of odd, but there’s a really cool use for it. If you need to track the data in the last record you changed—maybe you need an informal audit of which record just got changed while you’re scanning through other records—throw a global calculation field on your data entry layout. Then, no matter which record you’re looking at, you see the value of the last edited record in that field.

Logical Functions

The logical function group is a diverse and powerful lot. You get functions for making decisions (called conditional functions), learning about field values, and even evaluating calculations inside other calculations. This section covers all those possibilities. Along the way, you learn how to define and use variables, which act as placeholders while complex calculations go through their many steps.

Conditional Functions

This chapter began by posing an interesting challenge: You have a calculation field in the Invoices table called Total Due. It calculates the total amount due on an invoice by subtracting the sum of all payments from the total amount of the invoice. Can you modify the Total Due calculation to turn red when an invoice is past due?

The answer lies in the three conditional functions. Each one lets you specify more than one possible result. The function requires one or more parameters—called conditions or conditional expressions—that tell it which result to pick. The conditional functions—If, Case, and Choose—differ in how many possible results they support and what kind of condition they expect.

The If function

The first and most common conditional function is simply called If. The If function is the basic unit of decision making in FileMaker calculations. It’s the ticket when you have to decide between two choices, based on some criteria.

It looks like this:

	If ( Condition ; True Result ; False Result )

When you use the If function, FileMaker evaluates the condition looking for a Boolean result (true or false). If the condition has a true value, the function returns its second parameter (true result). If the condition is false, though, it returns the False Result instead. Here’s an example:

	If ( First Name = "Dominique" ; "Free" ; "$299.00" )

For example, this calculation returns Free if the First Name field matches “Dominique.” If it doesn’t match, then it returns $299.00 instead.

The Case function

Sometimes you need to pick from more than just two choices. Luckily, If has a cousin named Case that simply excels at such problems. For example, suppose you want to show one of these four messages on the top of your layout:

  • Good Morning

  • Good Afternoon

  • Good Evening

  • Go To Bed

You obviously need to choose between these messages based on the time of day. The If function doesn’t work very well for this problem because If only allows one condition and two possible results. You can nest If statements one inside the other, so that the false result is really another If statement. But nested If functions are really hard to read and even harder to tweak, so if you find that your business rules require a change in your calculation, you may rue the day you decided to use 12 nested Ifs to decide which discount your customers should get.

The Case function has this form:

	Case ( Condition 1 ; Result 1 ; Condition 2 ; Result 2 … )

You can add as many parameters as you want, in pairs, to represent a condition and the result to be returned if that condition is true. Because the conditions and results are sequential, and not nested, you can easily read a Case statement, no matter how many conditions you pile on. You can even add an optional parameter after the last result. This parameter represents the default result—the one FileMaker uses if none of the conditions were true.

Note

Since the Case function accepts several conditions, it’s entirely possible that more than one condition is true at the same time. If so, FileMaker chooses the first true condition when it picks a result.

To implement the greeting message described on Section 11.2.1.2, a calculation using the Case function might look like this:

	Case (
       Get(CurrentTime) > Time(4;0;0) and Get(CurrentTime) < Time(12;0;0);
	   "Good Morning";

	   Get(CurrentTime) > Time(12;0;0) and Get(CurrentTime) < Time(18;0;0);
	   "Good Afternoon";

	   Get(CurrentTime) > Time(18;0;0) and Get(CurrentTime) < Time(22;0;0);
	   "Good Evening";

	   "Go To Bed"
	)

In this calculation, the Case function checks first to see if the current time is between 4:00 a.m. and 12:00 p.m. If it is, the “Good Morning” value is returned. If not, it then checks whether the time is between 12:00 p.m. and 6:00 p.m., which would produce the “Good Afternoon” message. Finally, it checks to see if it’s between 6:00 p.m. and 10:00 p.m. If so, the user sees “Good Evening.”

You don’t need to specify a condition for the last result—“Go To Bed”—because if all the previous conditions are false, it must be time for bed. In other words, if it isn’t the morning, and it isn’t the afternoon, and it isn’t the evening, then it must be late at night. (If you need further help deciphering the above calculation, see the box on Section 11.3.2.2. On the other hand, if you’re so far ahead that you can see a better way to do it, see the box on Section 11.2.2.)

The Choose function

The Choose function is sort of the forgotten third member of the conditional trio. People don’t immediately grasp how to use it…so they don’t. But if you think of it as a value list with the choices coded into a calculation, you see how Choose can turn an awfully ugly Case function into a specimen of neatness.

It looks like this:

	Choose ( Condition ; Result Zero ; Result One ; Result Two … )

Unlike the other conditional functions, Choose doesn’t expect a Boolean expression for its condition. Instead, it looks for a number. The number tells it which of the results to choose: If Condition is zero, the function returns Result Zero; if it’s one, it returns Result One; and so on.

Imagine you have a Student table, and one of its fields is called GPA. This field holds the student’s current grade point average, as a number. You’d like to turn this number into a letter grade on the printed report.

Many FileMaker developers would immediately jump to the Case function to solve this problem. They’d do something like this:

	Case (
       GPA < 0.5; "F";
	   GPA < 1.5; "D";
	   GPA < 2.5; "C";
	   GPA < 3.5; "B";
	   "A"
	)

While this calculation gets the job done, you can do it more succinctly with the Choose function:

	Choose ( Round(GPA; 0); "F"; "D"; "C"; "B"; "A" )

When you turn the GPA value into an integer (using Round), it becomes a candidate for the Choose function. When the GPA is 3.2, FileMaker rounds it to three, and selects result number three: “B.” (Remember that the first result is for zero, so number three is actually the fourth result parameter. For more detail, see the box on Section 11.2.2.1.)

Note

This calculation uses the Round function, which you haven’t seen before. Round takes two numbers as parameters. It rounds the first value to the number of decimal places specified in the second parameter.

Constructing a Conditional Calculation

Now that you’ve seen the three conditional functions, it’s time to take a stab at that calculation way back from the beginning of this chapter: Make the Total Due turn red when the due date has passed.

When you’re trying to come up with a logical calculation, think about what information FileMaker needs to make the decision, and what action you want FileMaker to take after it decides. Then consider how best to do that using your database’s existing fields and structure. Your first decision is which conditional function to use.

Total Due calculation #1: using the If function

Most people’s first thought would be the If function, since the calculation needs to check if one condition is true:

  • Is the value of the Date Due field earlier than today’s date?

The calculation then takes the result of the If function and returns one of two possible results:

  • If it’s true that the due date has passed, display the total due in red text.

  • If it’s not true that the due date has passed, display the total due in black text.

In plainer English, the If condition checks to see if the due date is passed. If so, it returns a red result; if not, it returns a black result. In addition, the calculation needs to find the value to display in black or red—the total due. The full calculation might look like the following:

	If (
       // Condition
	   Get(CurrentDate) > Date Due and
	   // Calculate the total due here to make sure it's not zero
	   Invoice Amount > Total Paid;

	   // True Result
	   TextColor (
	      // Calculate the total due here for the red result
		  Invoice Amount Total Paid;
		  RGB(255;0;0)
       );

	   // False Result
	   // Calculate the black total due here
	   Invoice Amount Total Paid
	)

To put this calculation to work in your database, delete the calculation currently in the field definition for Total Due and type in this one. When the due date is passed, the value in your newly smarter Total Due field changes to red.

Note

Since the Total Due field already calculates the due balance, you may be tempted to take a shortcut: Create a new field that uses the If function to change the existing Total Due field to red, then plop that new field on the Invoices layout instead. But that would clutter your database with a superfluous field. And in a relational database especially, that kind of sloppy field swapping is only asking for trouble.

Total Due calculation #2: using the Case function

Lots of people like the Case function so much that they always use it, even in places where the If function is perfectly competent. You might choose to use Case if there’s any chance you’ll want to add some conditions to the statement later on. Instead of editing an If expression later, you can save time by using Case from the start (just in…case).

The same calculation using Case (and minus the helpful comments above) would look like this:

	Case (

	Get ( CurrentDate ) > Date Due and Invoice Amount > Total Paid ;

	TextColor ( Invoice Amount–Total Paid ; RGB ( 255 ; 0 ; 0 ) ) ;

	Invoice Amount–Total Paid

	)

Tip

With a single condition and default result, the syntax for If and Case are the same. So if you do need to change an If statement to Case later, simply change the word “If” to “Case” and add the conditions.

This calculation works as advertised, but it has a couple of weak points. First, it has to calculate the total amount due three times. That makes for three times as many chances to introduce typos and three times as many places to edit the “Invoice Amount–Total Paid” expression if you change the calculation later.

Second, Total Paid is an unstored calculation based on the sum of related records. That’s one of the slowest things you can ask a calculation to do. It may not matter much in this example, but in a more complicated situation, a calculation like this could slow FileMaker to a crawl.

In the next section, you’ll learn how FileMaker helps you write leaner calculations that are easier for you to read—and quicker for FileMaker to work through.

The Let Function and Variables

The Let function creates a temporary holder for a value, called a variable, which can be plugged into a calculation over and over again. You’ll do a little more work upfront to set up a variable, but that effort pays off with faster calculations that are easier to read, edit, and troubleshoot.

Defining Calculation Variables

In your Let function, you define a value and give it a name, and then use that name as often as you need throughout the calculation. In this case, you can calculate the amount due once, and store the result in a variable called Amount Due.

The Let function is unique among functions because it controls the way you write your calculation, not the result. Here’s an example:

	Let ( [ L = 5 ; W = 10 ; H = 3 ] ; L * W * H )

Like the Substitute function described on Section 10.3.3.4, Let uses bracketed notation. It really takes just two parameters. The first is a list of variable definitions. Each variable gets a name and a value using this format:

	Name = Value

Tip

FileMaker uses the terms “var” for Name and “expression” for Value in its manual and help files. The terms mean the same things, but “var” and “expression” sound much more impressive.

If you have more than one variable to define (as in the example above), put a semicolon between each one, and put them all in-between a pair of square brackets. You can use any calculation expression as the value.

In fact, the expression that determines the value of a variable can even use other variables that were defined earlier.

For example, the next calculation is perfectly legal. Its hours variable has a value of 240: 24 times the value of the Days variable:

	Let (
       [ Days = 10 ;
	   Hours = 24 * Days ;
	   Minutes = 60 * Hours ];

	   Minutes & " Minutes"
	)

The second parameter can be any calculation expression. This parameter is special because you can use any of the variables you’ve defined inside the expression, just like fields. In the first example above, there are three defined variables (L, W, and H); the expression then multiplies them together.

When FileMaker evaluates the Let function, it determines the value of each variable just once, and then plugs this value into the expression every time that variable is used. The result of a Let function is simply the result of its expression.

Total Due calculation #3: using Let

Your Total Due calculation can use the Let function to solve all its problems. Just put the Amount Due in a variable and use it throughout the calculation:

	Let ( Amount Due = Invoice Amount Total Paid ;
       If ( Get(CurrentDate) > Date Due and Amount Due > 0;
          TextColor ( Amount Due ; RGB(255;0;0) );
		  Amount Due
       )
	)

This version of the calculation is simpler, easier to change, and more efficient. You can’t beat that.

The Life of a Variable

Most variables last only as long as it takes FileMaker to work through the calculation, then they’re gone. This type of variable is called a local variable because they aren’t valid outside the Let function that calls them into existence. But you can also create a special variable, called a global variable, that lives beyond your calculation. Read on to see when to use each type.

Local variables

The variables you’ve written so far have all been local variables. Now it’s time to learn that local variables having shockingly short memories.

Local variables can lose their values even before a calculation is finished. If you write:

	Let ( Amount Due = Invoice Amount Total Paid;
       If (
          Get ( CurrentDate ) > Date Due and Amount Due > 0;
		  TextColor ( Amount Due ; RGB ( 255 ; 0 ; 0 ) );
		  Amount Due
		  )
	) & If ( Amount Due < 0 ; "CR" ; "" )

The calculation tries to use the Amount Due variable after the end parenthesis in the Let function. Anything that happens after that in the calculation is outside the Let function’s scope, so when you try to close the Specify Calculation dialog box on this calculation, FileMaker complains that it doesn’t know what that last Amount Due is supposed to be. One way to rewrite that calculation using a local variable is:

	Let ( Amount Due = Invoice Amount Total Paid;
       Case (
          Get ( CurrentDate ) > Date Due and Amount Due > 0;
		  TextColor ( Amount Due ; RGB ( 255 ; 0 ; 0 ) ;
		  Amount Due < 0 ; "CR" ; ""
		  )
	)

In this example, you’re including the last test condition within the scope of the Let function and you’ve switched to a Case function, so that you don’t have to read a set of nested If functions.

If you want the local variables you set inside calculations to follow the same naming conventions as variables you set in scripts (see Chapter 15), prefix their names with “$.” In that case, you’d write the calculation you just saw like this:

	Let ( $Amount Due = Invoice Amount Total Paid;
       Case (
          Get ( CurrentDate ) > Date Due and $Amount Due > 0;
		  TextColor ( $Amount Due ; RGB ( 255 ; 0 ; 0 ) ;
		  $Amount Due < 0 ; "CR" ; ""
		  )
	)

Notice that you have to include the prefix in the Let function and in the formula that follows it.

Global variables

But just as FileMaker gives you global fields, it also gives you global variables. Unlike local variables, global variables hold their results after the Let function is finished. To create a global variable, add a “$$” prefix to its name. Here’s the same calculation rewritten with a global variable:

	Let ( $$Amount Due = Invoice Amount Total Paid;
       Case (
          Get ( CurrentDate ) > Date Due and $$Amount Due > 0;
		  TextColor ( $$Amount Due ; RGB ( 255 ; 0 ; 0 ) ;
		  $$Amount Due < 0 ; "CR" ; ""
		  )
	)

The only difference you can see in the calculation is the $$ prefix. But the practical difference is vast: Global variable values remain until you change them (through another calculation or through a script), or until you close the file.

Tip

FileMaker’s Advanced Data Viewer (Section 19.1.1.4) eliminates guesswork by letting you check the contents of local and global variables.

You could run a script that checks to see if a Payment was made within 10 days of the Invoice Date and if it was, apply a 1% discount to the $$Amount Due field. Sure, you can do something similar with a straightforward calculation field, but in that case, it gets a little trickier to apply the discount to some of the records, but not to others. With a script, you can find the records you want to give a spur-of-the-moment discount, run the script on that found set, and you’re done.

Nesting Let Functions

As with other functions, you can nest Let functions inside one another. In fact, you can define a variable once, and then redefine it inside a nested Let function. The variable’s value changes while inside the nested Let function, then changes back when it ends. By the same token, you can define a variable with the same name as a field, and FileMaker uses the variable’s value while inside the Let function.

Here’s a very simple example of a Let function inside another Let function:

	Let ( X = 3 ;
       // only X is defined here
	   Let ( Y = 4 ;
          // X and Y are both defined here
		  X * Y
       )
	   // Only X is defined here too
	)

You can also use Let more than once in a single calculation without nesting:

	Let ( [X = 3; Y = 4] ; X * Y ) &
	Let ( units = "inches"; " " & units )
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.217.220