Вы находитесь на странице: 1из 202

Guide to the NAVISION

AL & DBMS

This guide includes the following three documents which have previously
been released as individual manuals:
AL Programmers Guide
AL Reference Guide
The NAVISION DBMS

This publication is subject to change without notice and does not represent any
commitment on the part of Navision Software a/s.
Navision Software a/s assumes no responsibility for any faults or shortcomings in
this publication.
The software described is supplied under license and must be used and copied in
accordance with the enclosed license terms and conditions.
According to existing Danish copyright legislation, it is against the law to
reproduce any part of this publication in any form or by any means, without the
permission of Navision Software a/s.

Navision is a registered trademark of Navision Software a/s.


Microsoft, Windows and Windows NT are trademarks of Microsoft Corporation.
Paintbrush is a registered trademark of Zsoft Corporation.

Copyright 1996 Navision Software a/s. All rights reserved.


ISBN 87-7849-064-2
Doc.no. W1-XU-BAG1-CB08-X
Published by Navision Software a/s.
Printed in Denmark 1996.

Table of Contents
Part 1, AL Programmers Guide 1
Examples and Notes 2
Notation for Datatypes in Expressions 2
Typographical Conventions 2
Compilation vs. Run Time Errors 3
Important Terms 3
Expressions in AL 4
AL Data Types 5
Constants 8
Elements in AL Expressions 8
Variables 9
Operators 13
Operator Hierarchy 14
Function Calls 15
Compound Statements 16
Conditional Statements 16
The AL Control Language 16
Repetitive Statements 18
EXIT Statements 20
WITH Statements 21
Comments in Programs 22
Type Conversion in Expressions 23
Type Conversion Mechanisms 24
Relational Operators 24
Yes/No (Logical) Operators 27
Introduction to Arithmetic Operators 27

Part 2, AL Reference Guide 37


Typographical Conventions 38
AL Functions grouped by category 39
Formatting of Numbers 126
Formatting of Dates 127
Formatting of Times 128
Formatting of Strings 129

Part 3, The NAVISION DBMS 163


The Aim of This Document 164
Physical vs. Logical Database 165
The Structure of the Database 165
Logical Organization 166
Fields and Records 167
Tables 170
The Key List 174
Companies 175
Special Database Fields 176
Important Tasks for the DBMS 179
Data Integrity 179
Database Security and Protection 187
Interfacing the DBMS 189
Table Handles 189
Current key 190
Filters 191
The DBMS Cache 193
Features for Increasing Performance 193
The Commit Cache 194
The Command Buffer 195

Appendix A, Sum Fields 197

Part

1 AL Programmers Guide
This document provides detailed information about the Application Language
(AL). The aim is that this document should serve as a guide for the novice, and
also as a handbook and refresher for the pro. The aim of this document is neither
to teach the reader how to program, nor to introduce specific programming
techniques. This means that a certain amount of knowledge about programming
in other languages, for instance Pascal or C, is assumed.
Because AL, unlike most other 4GLs is very similar to commonly used
programming languages, youll find the many Pascal books and other training
aids a valuable resource as you create your own applications.
The following list outlines some of the most powerful properties of AL. The AL
language:
Performs automatic type conversion between related types (not only between
numbers

Includes pre-defined types like Date and Time

Performs automatic attachment between database tables and records

Includes standard functions for:


- Database access
- Date calculations
- Handling strings
- Handling windows and menus
- Access to the operating system

Provides pre-defined types corresponding to the record layout in all the


database tables

Allows the use of a variable number of parameters in standard function calls

Allows the use of nested comments in programs

Performs automatic error handling.

This will also be the proper place to notify the reader of the following
delimitations compared to languages such as standard Pascal:
New data types cannot be defined

There are maximum sizes for variables

Arrays have a maximum of three dimensions

AL Programmers Guide 1

Typographical Conventions

Typographical
Conventions

This AL manual uses special typefaces to help you distinguish different types of
text. Table 1 illustrates these conventions:

Table 1: Conventions
Convention

Applies to

Examples

Monospaced Font

AL code examples

Number:= 6;

Times Font

Normal text

This is a sentence

Helvetica Font

Table bodies

Text in a table cell

ALL CAPS

AL keywords when
used in normal text and
in AL code examples

...by using the MESSAGE function...

Examples and Notes


In this document you will find paragraphs starting with the word Example. These
paragraphs provide examples related to the current context.
You will also find paragraphs starting with Note:. These paragraphs provide
additional information about what measures the user must take in order to get the
current operation to work properly. Often these paragraphs warns the user of
errors that might occur.

Notation for Datatypes in Expressions


All valid expressions in AL can be evaluated into a unique result having a
specific datatype. An expression like:

1204 + 123.46 - 45000

could (in this document) also be expressed in terms of the corresponding


datatypes, that is:

Integer + Decimal No. - Long Integer

This notation will only be used when the focus is on the datatypes rather than on
specific values. It is stressed that this notation is not a real AL notation, but
rather a notation which is intended to be used for descriptional purposes

AL Programmers Guide

Important Terms

Important
Terms

The aim of this section is to briefly discuss the following important terms:
Compilation vs. run time errors

Statements, expressions and operators

Compilation vs. Run Time Errors


When you have written some new AL code the system tells you if there is an
error in it at two points:
1. When you compile the AL code, the system checks the syntax of the code and
displays a message if it finds an error. The system will not compile your program until the syntax is correct.
2. Although the syntax of your program is correct, the code might still contain
errors. These are different from the compilation errors, as they only occur
when the program is run. They violate restrictions imposed by the system. A
typical run time error occurs if a function is called with an argument which is
outside a valid range.

Statements, Expressions and Operators


Consider the following AL code sample:
Amount := 34 + Total;

This individual code line is also called a statement. Table 2 illustrates how the
statement can be can broken into smaller elements.

Table 2: Elements in a statement


Element

Description

34 + Total

An expression. In this case the expression consists


of an arithmetic operator (+), and two arguments
(34 and Total), which also could be called subexpressions. All valid AL expressions can be evaluated into a specific value

:=

The assignment operator. When the right-hand side


expression has been evaluated into a value, this
operator is used to assign (store) the value in the
variable Amount

Amount

This is called a variable (or sometimes an identifier). Is used as a memory in order to stores values.

The above discussion is only intended to serve as a brief introduction to some


basic terms.

AL Programmers Guide

Expressions in AL

Expressions
in AL

Understanding the concepts of expressions, is fundamental when programming in


the AL language. This section describes expressions and how they are used.
An expression can be used as an argument to an AL function. Consider the AL
statement below:
Date := DMY2DATE(31, 12, 1996);

This function takes three simple expressions as arguments, that is 31, 12 and
1996.
An AL expression is a group of characters that can include data values, variables,
arrays, operators, and functions (which can be evaluated into a value). All valid
AL expressions can be evaluated into a specific value which has an associated
data type.
All expressions in AL are built from:
Constants

Variables

Operators

Functions

Depending on the expression the evaluation will lead to a value from one of the
AL data types: Yes/No, Option, Integer, Long Integer, Decimal No., Date, Time,
Text or Code.
Table 3 shows some typical expressions.

Table 3: Typical expressions


Expression

Evaluates to...

'Welcome to Hawaii';

The string 'Welcome to Hawaii'

'Welcome' + ' to Hawaii';

The string 'Welcome to Hawaii'

43.234;

The number 43.234

ABS(-7234)

The number 7234a

len1 < 618;

TRUE or FALSE depending on the value


of len1

a. The ABS function calculates the absolute value of the argument.


The first line shows a text string which is evaluated into itself. The second line
evaluates into a concatenation of the two strings. The third line shows a decimal
number, which is evaluated into itself. The expression in the fourth line contains
a function, which with the given argument is evaluated into the number 7234.
The last line shows a comparison between a variable and a numerical constant.
The above was just typical examples of how AL expressions evaluated into a
value having some AL data type. The following section describes the AL data
types in further detail.

AL Programmers Guide

Expressions in AL

AL Data Types
This section describes the data types used in AL. The data types are divided into
three main categories, named: Simple Data Types, Composite Data Types and
Descriptional Data Types.

Simple Data Types


The simple data types are fundamental when describing real world data in AL.
The simple data types are also used as building blocks for the composite data
types to be discussed in the following section.
Yes/No. Denotes a so-called Boolean data type. May have the values TRUE or
FALSE.
Option. Denotes integers ranging from 0 to 255.
Integer. Denotes integers ranging from -32,767 to 32,767.
Long Integer. Denotes integers ranging from -2,147,483,647 to 2,147,483,647.
Decimal No. Denotes decimal numbers ranging from -10+63 to +10+63. The
exponent ranges from -63 to +63. Decimal numbers are held in memory with 18
significant digits.
Date. Denotes dates ranging from January 1, 1980 to December 31, 2059. An
undefined date is expressed as 0D. All dates have a corresponding closing date.
The closing date for a given date is regarded by the system as a period following
the given date, but before the next normal date, thus closing dates are sorted
immediately after the corresponding normal date, but before the next normal
date.
Time. Denotes a time. An undefined time is expressed as 0T. Any time in the
range 00:00:00 to 23:59:59.999 is valid.
Note: in the context of the following two data types (Text and
Code), it is important to distinguish between the maximum length
of the string, and the actual length of the string. The maximum
length can be seen as the upper limit for the number of characters in
the string, while the actual length describes the number of
characters used in the string.
Text. Denotes a text-string. The maximum length of the string ranges from 1 to
132 characters. The length of a variable of type Text, corresponds to the number
of characters in the text. An empty text string thus has the length 0.
Table 4, Assignments of Text strings, on page 5 illustrates some typical
examples of text strings. In these examples it is assumed that the variable t is of
the type Text and has the maximum length 6.

Table 4: Assignments of Text strings


Assignment

Results in...

t := 'AbC';

The variable t now contains 'AbC'.

t := '123456abx';

Results in a run-time error, because the length (9)


exceeds the maximum length (6).

AL Programmers Guide

Expressions in AL

Code. Denotes a special type of text string. When a given text is assigned to a
code type variable, the text is transformed to uppercase, and furthermore any
leading and trailing spaces are removed. The maximum length of a code type
variable ranges from 1 to 132 characters. The length of a code type variable
always corresponds to the number of characters in the text without leading and
trailing spaces. Table 5 shows some typical examples of Code string assignments.
In the examples it is assumed that the variable c has the type Code, and the
maximum length 4.

Table 5: Assignment of values to Code strings


Assignment

Results in...

c := 'AbC';

The variable c now contains 'ABC'. The length is 3.

c := '1';

The variable c now contains '1'. The length is 1.

c := '';

The variable c now contains '' (empty string). The


length is zero.

c := ' 2 ';

The variable c now contains '2'. The length is 1.

c := '1 2';

The variable c now contains '1 2'. The length is 3.

Composite Data Types


Based on the simple data types described above, it is possible to define some data
types with a more complex structure, such as Records and Arrays.
Record. A Record is a complex data type, which consists of a number of
simpler elements. These are called fields. Each field can be used to store values
of a certain data type. The fields are accessed using the record name, a dot and
the field name. A record is typically used to hold information about a fixed
number of properties. A typical example:
Assume that a company needs a convenient way to organize information about
their employees. One way would be to register all relevant information in a table
in a database. In this table the data for each employee could be stored in records
containing appropriate fields, for example fields for Name, Age, Address e.t.c.
Array. Based on the simple data types and Records presented above, it is
possible to create one, two-and three-dimensional variables. Each dimension of a
variable can contain up to 999 elements. However, a variable must never be
larger than 32 Kb. Arrays are always indexed from 1 to (and including) the size
of the dimension(s). If one tries to index outside the range of the dimensions of
an array, a run-time error occurs. Some typical examples of arrays:
Assume that I is a one-dimensional array variable of the Integer type, with the
dimension 10.
In order to index the first element, use I[1].

In order to index the last element, use I[10].

Assume that D is an array variable of type Date with the dimensions 2x3x4. Then
D has 24 elements.
In order to index the first element, use D[1,1,1].

AL Programmers Guide

Expressions in AL

In order to index the last element, use D[2,3,4].

Descriptional Data types


In order to describe the syntax of the AL-language, some additional data types
are needed. It is important to stress that these are not real system data types, but
only are used in this document for descriptional purposes.
Number. This data type denotes the union of the Option, Integer, Long Integer
and the Decimal No. data types
String. This data type denotes the union of the Text and Code data
Table 6 summarizes the correspondance between the descriptional data types and
the simple AL data types.

Table 6: The descriptional data types used


in this manual
Numbers

Option
Integer
Long Integer
Decimal No.

Strings

Text
Code

AL Programmers Guide

Elements in AL Expressions

Elements in AL
Expressions

The previous chapter has introduced you to the notion of expressions and
futhermore the data types used in AL was described. The aim of this chapter is to
present the basic elements of AL expressions. In short the following sections
will discuss the topics below:
Constants

Variables

Operators.

Functions

We start by defining ranges and properties of constants in AL.

Constants
A constant is the simplest type of operand used in AL. The value of a constant
cannot be changed during the execution of the code. Constants can be defined for
each of the simple data types in AL.
Note: Before we define the valid ranges for the constants below,
we have to warn the reader: the numbers 32,767; 2,147,483,647
and 999,999,999,999,999.99 below cannot be entered in the AL
system using commas. The commas are only used to increase the
legibility in this document. If numbers are entered in the AL editor
using commas, a compilation error will occur.
Yes/No (Boolean) Constant. A Yes/No constant may either have the value
TRUE or FALSE.
Integer Constant. An Integer constant may have a value between -32,767 and
32,767.
Long Integer Constant. A Long Integer constant has a value in one of the
following intervals:
From -2,147,483,647 to -32,768 and
From 32,768 to 2,147,483,647
If the number is between -32,767 and 32,767, it is, as mentioned above, an
integer.
Decimal No. Constant. A Decimal No. constant must contain a decimal point
'.' and have at least one digit to the right of the decimal point (for example the
digit '0'). A constant of type Decimal No. can be used to represent decimal
numbers between -999,999,999,999,999.99 and 999,999,999,999,999.99 with 18
significant digits.
Date Constant. A Date constant is written as six digits followed by the letter
'D' (the date constant expressing 'undefined date' is, however, entered as '0D').
The six digits specify the date in the format MMDDYY.
Time Constant. A Time constant is written as nine digits followed by the letter
'T' (the 'undefined time' constant is, however, entered as '0T'). The nine digits
specify the time in the format HHMMSS[.XXX], that is a 24 hour format with an
optional part specifying thousandths.
Text Constant. A Text constant is a character string that is placed in
apostrophes ('). '' indicates the empty text string.
Examples

AL Programmers Guide

Elements in AL Expressions

Table 7 illustrates different types of AL constants.

Table 7: Examples of AL constants


Constant

Description

TRUE

Yes/No (Boolean) constant

231

Integer constant

50000

Long Integer constant

-23.7

Decimal No. constant

122196D

Date constant (December 21, 1996)

141230T

Time constant (the time 14:12:30)

'ABC

String constant

Variables
There are two types of variables in the AL system: User-defined variables and
Implicit variables.
Variables are defined by the user when new AL code is implemented. These userdefined variables are local to the AL module for which they have been defined.
These variables can be used to store information at run time, and the values can
be changed as desired.
Furthermore, a number of predefined variables are provided by the system. These
variables are automatically maintained by the system and are called Implicit
variables
The implicit variables can in many respects be used just like normal user defined
variables, but as the values of the implicit variables are maintained automatically
by the system, some restrictions should be observed.
The running system will sometimes enter a code module, for example entryprocessing code for a table. Before the module is entered, the system has
automatically assigned values to the associated implicit variables, and in the
module (in the AL triggers and the local functions) the values of these variables
can be used.
During the execution of AL triggers and local functions, the implicit variables
can be used just like normal variables (new values can be assigned to them) that
is, the values of the implicit variables are not updated by the system while the AL
code is being executed, but only before the module is entered. Some other
important properties are best explained using an example:
Example
A typical implicit variable is CursorPos, which describes the position of the
cursor. This implicit variable will be available in the AL triggers and the local
functions in some code modules, and can be read and changed as desired by the
user. But although the value of the implicit variable can be changed by the AL
code, the old value of CursorPos will be lost the next time the module is entered,

AL Programmers Guide

Elements in AL Expressions

as the implicit variables are automatically maintained by the system.


Furthermore it is also important to understand that the value in an implicit
variable like CurserPos does not propagate backwards, in other words the user
cannot use the implicit variable CurserPos to set the position of the cursor.

Variable Names
Variable names must be unique, that is, two user defined variables with the same
name are not allowed in an AL module. Furthermore you cannot have user
defined and implicit variables with the same name. Uppercase and lowercase
letters are interpreted in the same way, that is Smith and SMITH are taken to be
referring to the same variable. In standard Pascal notation a variable name (an
identifier) can only be written as an unbroken word. This notation is extended in
AL; here it is also possible to use special characters (for example spaces) in a
variable name.
Observe the following basic restrictions:
The maximum length of a variable name is 20 characters.

A variable name must not correspond to an AL function name or to any


reserved word. Please note that this rule applies both for uppercase and
lowercase spellings, for example neither BEGIN nor begin are valid.

All ASCII characters are valid in variable names, except the following:
Control characters (ASCII 0-31, 255)

The characters @ (ASCII 64) and " (ASCII 34)

When using a variable name, observe the following:


The characters cannot be combined freely. Normally the first character will
be:
- a letter in the range: a..z, A..Z (ASCII 97-122, 65-90), or
- an underscore (ASCII 95).
...followed by a maximum of 19 characters, which can be either:
- a letter a..z , A..Z (ASCII 97-122, 65-90)
- an underscore (ASCII 95), or
- digits in the range 0..9 (ASCII 48-57)
As already mentioned, it is also possible to include one or more special
characters (spaces e.t.c) in a variable name in AL, but if the user desires to use
variable names which contain one or more special characters, then the entire
variable name must be put in double quotes.
As long as a variable name is encapsulated in double quotes, it can contain any
mix of letters, digits and special characters.
Note: The double quotes are not part of the variable name, but are
necessary in order to avoid a compile-time error.
Examples
The following variable names are valid:

10

Customer

StockGroup1

AL Programmers Guide

Elements in AL Expressions

"1st AddressLine"

"Purchase/Sales"

"Sales In GBP"

" YesCrazy Name13"

...while the following variable names are invalid:


34467

23"Tubes

Stock Group4

"Sale"s in GBP"

@-Names

END

Initialization
Internal variables are automatically initialized before an AL module is executed.
A Yes/No variable is set to FALSE, Number type variables are set to the default
value zero, while strings (Text and Code) are initialized to the value (the empty
string). Date and Time variables are set to respectively the undefined time 0T and
the undefined date 0D.
As previously mentioned the system automatically handles the implicit variables.
This also includes the necessary initialization. This means that no actions are
required by the user before the implicit variables can be used.

Assignment and Type Conversion


Assignment of values can be performed in one of two ways.
1. As parameter assignment, for example FUNCTION(Expression). The resulting datatype of the evaluation of the expression must correspond to a specific
datatype or have a type which can be converted automatically to the correct
type. (For a detailed discussion about evaluation and type conversion in
expressions, please see Type Conversion in Expressions on page 23.)
2. By using the assignment operator ':=', for example Variable := Expression.
Generally, the resulting datatype of the evaluation of the right-hand side
expression1 must be of the same type as the variable (left operand) or have a
type which can be converted automatically to the type of the left operand.
Automatic type conversion in assignments automatically takes place when:
1. A parameter in a function call does not have the correct type. This occurs for
instance if a function which is supposed to be called with an integer argument, is called with for example a Decimal No. argument.
2. The evaluation of the expression at the right-hand side of an assignment operator (:=) results in a type which differs from the type of the variable at the
left-hand side.
The automatic type conversion in assignments can take freely place between the
following Number datatypes, provided overflow does not occur:
Numbers:

Option Integer Long Integer Decimal No.

The automatic type conversion in assignments can also take freely place between

AL Programmers Guide

11

Elements in AL Expressions

the String datatypes:


Strings:

Code Text

All the above has been based on simple variables. Nevertheless the same
assignment rules apply for arrays in AL. Furthermore, if the left operand in an
assignment (the variable) is an array, the dimension(s) of the right-hand side
expression must correspond to the dimension(s) of the variable.
Note: The type conversion that takes place in assignments can
cause run-time errors even though the types are convertable. A runtime error can occur in an assignment if the converted value is
outside the valid range for the left hand side variable.
Correspondingly a run time error can occur if the converted value is
outside the valid range for a parameter in a function call.
The following shows some examples of assignments:
Example
Let the variable A be defined as a one-dimensional array with 4 elements of the
type Text with the maximum length 10. A value could be assigned to the second
element in the array as shown below:
A[2]:= '0123456789';

Example
Let the variables Result, Amount and Total be defined as:
Result has the type Option.

Amount and Total both have the type Decimal No.

Consider the following assignment statements:


Amount := 10;
Total := 4;
Result := Amount + Total;

The above code can always be compiled, but a run-time error will occur if the
result of the right-hand side expression 'Amount + Total' exceeds the valid range
of the datatype of the left-hand side variable (outside the range of 0 to 255).

Valid Assignments
Table 8, Valid assignments in AL, on page 13 provides an overview of whether
it is possible to assign the value of an expression of a given type to a variable of a
given type. The rows describe the type of the variable, and the columns describe
the type of the expression. The following signs are used in the table:

12

Yes

The assignment is valid.

(Yes)

The assignment is valid, but overflow


may occur.

No, the assignment is invalid

AL Programmers Guide

Elements in AL Expressions

Table 8: Valid assignments in AL


Expression Type

Variable
Type

Valid?

YesNo

Option

Integer

Long
Integer

Decimal
No.

Date

Time

Text

Code

YesNo

Yes

Option

Yes

(Yes)

(Yes)

(Yes)

Integer

Yes

Yes

(Yes)

(Yes)

Long Integer

Yes

Yes

Yes

(Yes)

Decimal No.

Yes

Yes

Yes

Yes

Date

Yes

Time

Yes

Text

(Yes)

(Yes)

Code

(Yes)

(Yes)

Operators
Operators can be used in expressions in order to combine, investigate and
manipulate values and data elements. In the following the function of the
operators in AL are described.
Table 9 shows the valid operators in AL.

Table 9: Operators in AL
AL Operator

Meaning

Fields in records

()

Parentheses

[]

Indexing

Addition

Subtraction or negation

Multiplication

Division

DIV

Integer division

AL Programmers Guide

13

Elements in AL Expressions

Table 9: Operators in AL
MOD

Modulus

>

Greater than

>=

Greater than or equal to

<

Less than

<=

Less than or equal to

Equal to

<>

Not equal to

AND

Logical Conjunction

OR

Logical Disjunction

NOT

Logical negation

The '+' and the '-' operators can be used both as unary and binary operators, the
'NOT' operator only as an unary operator, while all other operators are binary.
Most of the above operators can be used on different datatypes. The action of
these operators may depend upon the datatype of expression they are used on. A
typical example:
Example:
The '+' operator can be used as a binary operator in for example:
Number + Number, which returns the sum of the numbers, that is a result of
the type Number.

String + String, which returns the concatenation of the strings, that is a result
of the type String.

Furthermore the '+' operator can be used as an unary operator to indicate sign, for
instance:
+ 34545
One of the following chapters Type Conversion in Expressions on page 23 will
show how the function of the operators vary according to the data types of the
applied arguments.

Operator Hierarchy
The operators just discussed, are organized in a hierarchy which decides the
order of evaluation of the operands in a given expression. The list below shows
the precedence order of the AL operators:
1. .(fields in records), [] (indexing), () (parentheses)
2. NOT, - (unary), + (unary)
3. *, /, DIV, MOD, AND
4. +, -, OR
5. >, <, >=, <=, =, <>
Example
This example illustrates the effect of the operator hierarchy. The expressions

14

AL Programmers Guide

Elements in AL Expressions

below, which apparently are the same, will produce different results.
The expression

2+3 *4

is evaluated to

14, whereas

the expression

(2 + 3) * 4

is evaluated to

20.

Function Calls
AL covers a number of functions for different purposes, such as string handling,
text formatting, database handling and so on. Some of these functions differ from
standard Pascal, as it is possible to use a variable number of parameters. In a
function call, the parameters are separated by commas, and the optional
parameters may be omitted from the right.
This means that if the function has, for instance, 3 optional parameters, then it is
not possible to omit the second without omitting the third:
Example
The fictitious function:
FUNCTION([Optional1] [, Optional2] [, Optional3])

can then be called, as:


FUNCTION(Optional1, Optional2)

but not as:


FUNCTION(, Optional2, Optional3)

Example
A typical example of a normal Pascal-like function is ABS.
Value := -1033;
PositiveValue := ABS(Value);

{A negative integer value}


{Calculate the positive value
1033}

ABS is a typical example of an AL function with a fixed number of parameters


(1).
Example
The function DMY2DATE is a typical example of a function which can be called
with a variable number of parameters:
NewDate := DMY2DATE(5, 11, 1992);{Returns the date November
5, 1992}

Depending on the use of the DMY2DATE function 1, 2 or 3 parameters can be


passed to the function, as the second and third parameter are optional. When the
second and third parameters are not used, the system uses values from the system
date as default.

AL Programmers Guide

15

The AL Control Language

The AL
Control
Language

This section describes the basic constructs in the control language in AL and how
to use them. All the AL programs you create consist of one or more statements.
The statements are executed sequentially in top-down order. However, you will
often need to control the direct top-down flow of the execution. You might
desire, for one reason or another, to repeat the execution of one or more
statements a number of times, or in other situations you might desire to make the
execution of a certain statement conditional and so on.
Various control constructs are available in AL, just like in Pascal. These control
constructs can be used to design very complex control structures, in order to take
different actions according to the current state of the execution.
The control constructs in AL are divided into the following main groups:
Compound Statements

Conditional Statements

Repetitive Statements

WITH Statements

Compound Statements
In some cases the AL syntax will only allow use of a single statement. If you
desire to execute more than one simple statement in such a case, the statements
can be turned into a compound statement, by 'encapsulating' the statements
between the keywords BEGIN and END. The syntax is:
BEGIN
<Statement 1>;
<Statement 2>;
.
.
<Statement n>;
END

The individual statements are separated by a semicolon. In AL and Pascal a


semicolon is used to separate statements, and not as in other programming
languages as a terminator symbol for a statement. Nevertheless an extra
semicolon before an END does not cause an error, because it is interpreted by
the compiler as an empty statement.
The above BEGIN END structure is also called a block. Blocks can be very
useful in connection with the other control constructs to be discussed in the
following.

Conditional Statements
By using a conditional statement, you can specify a condition and one or more
commands to be executed according to if the condition is evaluated to TRUE or
FALSE. There are two types of conditional statements in AL:
1. IF THEN [ELSE], when there are a maximum of 2 selections.
2. CASE, when there are multiple selections.

IF THEN ELSE

16

AL Programmers Guide

The AL Control Language

This statement type has the following syntax:


IF <Condition> THEN <Statement1> [ ELSE <Statement2> ]

Which means:
If <Condition> is true, <Statement1> is executed. If <Condition> is false,
<Statement2> is executed. As defined earlier, the square brackets around ELSE
<Statement2> means that this part of the statement is optional.
This statement is used when different actions are to be executed, depending on
the evaluation of the <Condition>
It is possible to build even more complex control structures by nesting IF THEN
ELSE statements. A typical example is:
IF <Condition1> THEN IF <Condition2> THEN <Statement1> ELSE
<Statement2>

If <Condition1> is false, nothing is executed. If <Condition1> and <Condition2>


are both true, <Statement1> is executed. If <Condition1> is true, and
<Condition2> is false, <Statement2> is executed. Please notice that a semicolon
preceding an ELSE is not allowed.
Several nested IF THEN ELSE statements may seem confusing but a general rule
is that an ELSE belongs to the last IF that lacks an ELSE.
Here are some examples of IF THEN ELSE statements:
Example
Illustration of an IF statement without the optional ELSE part:
IF Amount < 1000 THEN Total := Total + Amount;

Example
(1).
(2) IF Amount < 1000
(3) THEN BEGIN
(4)
IF I > J THEN Max := I
(5)
ELSE Max := J;
(6)
Amount := Amount * Max;
(6)
END
(7) ELSE
(8).

A common error for the AL novice, is to put an extraneous semicolon at the end
of a line before an ELSE (line 4). As mentioned above this is not valid according
to the syntax of AL, as the semicolon is used as a statement separator. (The end
of line 4 is inside the inner IF statement).

CASE
The syntax for the CASE statement is:
CASE <Expression> OF
<Value set 1> : <Statement 1>;

AL Programmers Guide

17

The AL Control Language

<Value set 2> : <Statement 2>;


...
...
<Value set n> : <Statement n>;
[ELSE <Statement n+1>]
END;

In the above definition the <Expression> cannot be a record, and the <Value set>
must be an expression.
CASE statements are also called multi-option statements and are typically used
when a selection between more than two different actions are to be made. The
function of the CASE statement is as follows:
The Expression is evaluated, and the first value set matching, causes an
associated statement to be executed.
If none of the value sets matches the value of the expression and the ELSE part
has been omitted, no action will be taken; but if the optional ELSE part is used,
then the associated statement will be executed.
The type of the value sets must be the same as the type of <Expression> or at
least convertable to the same type.
Note: The datatype of the value sets will be converted to the same
datatype as the evaluated <expression>, if necessary. Due to this
type conversion, an overflow may occur at run-time, if the resulting
datatype cannot hold the values of the value sets.
Example
This AL code sample will print various messages depending on the value of
Number. If the value of Number does not match any of the entries in the CASE
structure, the ELSE entry will be used as default.
CASE Number OF
1,2,9: MESSAGE('1, 2 or 9');
3,89,17: MESSAGE('3, 89 or 17');
ELSE MESSAGE('Neither 1, 2, 3, 9, 17 nor
89');
END

Repetitive Statements
A repetitive construct is also known as a loop. The looping constructs in AL are:
FOR, which repeats the inner statements until a counter variable equals the
maximum or minimum value specified.

WHILE, which repeats the inner statements while the specified condition is
TRUE. The statements in a loop of this type is repeated 0 or more times.

REPEAT, which repeats the inner statements until the specified conditions
evaluates to FALSE . The statements in a loop of this type is always executed
at least one time.

FOR TO/DOWNTO
The syntax for the FOR TO (and FOR DOWNTO) statement is:

18

AL Programmers Guide

The AL Control Language

FOR <Control Variable> := <Start Number> TO <End Number> DO


<Statement>

<Control Variable>, <Start Number> and <End Number> must be Yes/No,


Number, Time or Date datatypes.
FOR statements are used when a code block is to be executed a specific number
of times. A control variable is used to control the number of times the code block
is executed. The <Control Variable> may be increased or decreased by one,
according to whether TO or DOWNTO is used.
When using a FOR TO loop, the <Statement> will not be executed if the
<START NUMBER> is greater than the end value. Correspondingly, the
<Statement> will not be executed in the FOR DOWNTO loop, if the start value is
less than the end value.
Note: If the user changes the value of the control variable inside
the FOR loop, the behaviour of the system is not predictable.
Furthermore the value of the control variable is undefined outside
the scope of the FOR loop.

Note: When declaring the type of the <Control Variable> it should


be noticed that when the system executes the FOR statement, the
<Start Number > and <End Number> will be converted to the same
datatype as <Control Variable>, if necessary. Due to this type
conversion a run-time error may occur. This is illustrated in the
following example:
Example
The following FOR loop uses a control variable Count of the type Integer.
FOR Count := 1000 TO 1000000 DO

When the above statement is executed, a run-time error will occur, because the
system tries to convert the start and end values to the same type as the control
variable; but as Count has been declared as an Integer variable, an error will
occur when 1000000 is to be converted, because this end value is outside the
valid range for Integers.
Example
This example illustrates nesting of FOR statements:
FOR I := 1 TO 5 DO
FOR J := 1 TO 7 DO
A[I,J] := 23;

The two FOR statements above could be used to initialize all elements in a 5 x 7
array with the value 23;

WHILE DO
The WHILE DO statement has the following syntax:

AL Programmers Guide

19

The AL Control Language

WHILE <Condition> DO <Statement>

If <Condition> is true, <Statement> is executed repeatedly, until <Condition>


becomes FALSE. If <Condition> is false from the start, <Statement> is never
executed.
When a block of code is to be repeated as long as an expression is true, the
WHILE DO statement may come in handy.
Example
The AL code below increases the variable i until it equals 1000:
WHILE i < 1000 DO i := i + 1;

REPEAT UNTIL
The syntax for the REPEAT UNTIL statement is:
REPEAT <Statement> UNTIL <Condition>

<Statement> will be executed repeatedly until <Condition> is TRUE.


This construct might at first glance seem to function just like the WHILE
construct, but as the REPEAT UNTIL statement is executed from left to right, it
is easily seen that the <Statement> always will be executed at least one time no
matter what the <Condition> is evaluated to . This contrasts to the WHILE
construct which performs the evaluation before the <Statement> is executed.
This implies that if the first evaluation of <Condition> returns TRUE, then no
statements will be executed.
Example
A typical use of the REPEAT UNTIL construct:
S:= ''; {The empty string}
REPEAT
S:= S + ' ';
{Accumulate space characters in S}
UNTIL STRLEN(S) = 30;

The above AL code will store 30 spaces in the (Text) variable S.

EXIT Statements
The EXIT statement is yet another construct you can use to control the flow of
the execution. The syntax of an EXIT statement is:
EXIT([<Value>])

An EXIT statement is used to interrupt the execution of an AL trigger . The


interruption will be effected even though the execution is inside a loop or a
similar structure. The EXIT statement is also used when a local function is to
return a value: EXIT(Value).
Using EXIT without a parameter in a local function, corresponds to using the
parameter value 0, that is, the AL function will return the value 0 or '' (empty

20

AL Programmers Guide

The AL Control Language

string).
A compile-time error will occur if EXIT is called with a return parameter from:
1. System defined triggers
2. Local functions, which is not supposed to return a value
Example
The following illustates the use of the EXIT statement in an arbitrary local
function. Assume that the IF statement is used to detect an error. If the errorcondition is met, the execution is stopped and the local function returns the errorcode 1.
FOR I := 1 TO 1000 DO
BEGIN
IF Amount[I] < Total[I] THEN EXIT(1);
A[I] := Amount[I] + Total[I];
END;

WITH Statements
The syntax for the WITH statement is:
WITH <Record> DO <Statement>

When working with records, addressing is carried out as the record name, a dot
and the field name: <Record>.<Field>
If you continuously work with the same record, then you can use WITH
statements. When you use a WITH statement it will not be necessary to specify
the record name more than once.
Within the scope of <Statement>, fields in <Record> may be addressed without
specification of the record name.
Several nested WITH statements may be used. In case of identical names, the
inner WITH will overrule the outer WITH-statements. The only restriction on the
use of nested WITH statements, is that WITH cannot be used on an element in
an array of records.
Example
This example shows two ways of writing the same code:
CustomerRec.No:= '1234';
CustomerRec.Company:= 'Windy City Solutions';
CustomerRec.Manager:= 'Joe Blow';
CustomerRec.Address:= '1241 East Druid Avenue';
CustomerRec."State and Zip":= 'Chicago, IL 60079';

another way of expressing the same, is:


WITH CustomerRec DO
BEGIN
No:= '1234';
Company:= 'Windy City Solutions';
Manager:= 'Joe Blow';
Address:= '1241 East Druid Avenue';

AL Programmers Guide

21

The AL Control Language

"State and Zip":=


END;

'Chicago, IL

60079';

Comments in Programs
You can insert comments about the code, or 'outcomment' parts of your code
temporarily to prevent its execution.
A comment starts with '{' or and ends with '}' or ''. Any number of nested
comments may occur. In such cases, the comment runs from the first comment
start to the last comment end.
The system does not distinguish between the '{ '- and ''- type comments, that is,
it is possible to start a comment using a '{' and end it with a ''. The user should
be aware of this when using lines to draw figures inside a comment.
Example:
{
This is a sample comment which is ignored by the AL compiler
}

Example:
{ This comment { is partly inside } another comment }

Example:
The last example illustrates how you should not do when you a apply a box
comment:
A
B
C
D

:=
:=
:=
:=

34;
56;
345;
781;

This is a box comment

By placing the box comment to the right of the AL statements, the third and
fourth lines are assumed by the system to be part of the comment, that is, only A
and B are assigned values, while C and D are not. The solution is to separate the
box comment from the AL statements, like:
A := 34;
B := 56;
This is a box comment
C := 345;
D := 781;

22

AL Programmers Guide

Type Conversion in Expressions

Type Conversion
in Expressions

Consider the following statement:


Sum := 1556 + 56000;

A statement of this type involves one or two type conversions. The right-hand
side of the statement involves the evaluation of the expression 1556 + 56000 (in
other words Integer + Long Integer). In order to evaluate this expression, the first
operand (1556) will have to be converted from Integer to Long Integer. The
addition operator will then return a Long Integer result. But if the type of the lefthand side variable has been declared as for instance Decimal No., a type
conversion from Long Integer to Decimal No. has to take place before the value
can be assigned to Sum. The latter category of conversion has already been
discussed. See Assignment and Type Conversion on page 11.
In this section we will consider the type conversion that (might) take place when
expressions are evaluated.
Initially some general rules can be outlined:
When asked to evaluate an expression of mixed datatypes, the system will (if
possible) always convert, a least one of the operands to a more general
datatype.

The datatypes in the two main groups Numbers and Strings, can be ranked
from 'most general' to 'least general', as defined below.

Numbers

Most General

Decimal No
Long Integer
Integer

Least General

Option

Strings
Text
Code

The most general datatypes include all possible values from the less general
datatypes. The above should be interpreted as follows: a Decimal No. is
more general than a Long Integer, which again is more general than an
Integer and so on.

Type conversion can take place in some cases although two operands have the
same type. A typical example is an expression like:
Integer + Integer
In this case the system will convert both the operands to Long Integers, and
the resulting datatype will be Long Integer.

The above rules can be illustrated by some examples.


Example:
Evaluation of a Number expression:
Integer + Decimal No.

This expression contains two sub-expressions of different type. In order to add


these, the system will convert the left sub-expression to Decimal No.:

AL Programmers Guide

23

Type Conversion in Expressions

Decimal No.+ Decimal No

When the left-hand side sub-expression has been converted, the expression can
be evaluated, and the resulting data type will be Decimal No.:
Decimal No. + Decimal No. Decimal No.

Example:
Evaluation of a String expression:
Text + Code

This expression contains two sub-expression to be concatenated. In order to do


this, the system will convert the sub-expression of the least general data type
(Code) to the most general data type (Text).
Text + Text

When the right-hand side argument has been converted, the expression can be
evaluated, and the resulting datatype will be Text.
Text + Text Text.

Type Conversion Mechanisms


We will now turn to a detailed discussion of the type conversion mechanisms for
the operators in AL. We start by dividing the operators into some main
categories:
Relational operators

Logical operators

Arithmetic operators

The following sections aim at discussing the properties of the operators in AL.
For each category of operators the valid datatypes for the arguments are
discussed, and furthermore we define the resulting data types when evaluating
expressions.
We will start with a discussion of the so-called relational operators, as these are
common to most of the AL datatypes.

Relational Operators
The relational operators are used to compare expressions. Table 10 defines the
evaluation rules for relational operators. The rules assume that the expressions
evaluates to comparable types. Please refer to Table 11, Valid uses of relational
operators, on page 26 for an overview of which datatypes are comparable.

24

AL Programmers Guide

Type Conversion in Expressions

Table 10: Relational operators


Operator

Name

Expression

Resulting
Datatype

>

Greater than

Expr > Expr

Yes/No

<

Less than

Expr < Expr

Yes/No

<=

Less than or equal

Expr >= Expr

Yes/No

<>

Not equal to

Expr <> Expr

Yes/No

Equal to

Expr = Expr

Yes/No

Note: When using relational operators the following should be


observed: Upper and lower case letters in Strings are taken to be
different. The comparison is carried out by means of the system's
build in character comparison table, that is, not by comparing true
ASCII characters
Table 11, Valid uses of relational operators, on page 26 describes the valid uses
of the relational operators, and the resulting datatype when an expression
containing relational operators has been evaluated. The invalid combinations of
types for relational operators are indicated by a dash.
Table 11 should be interpreted as follows1: the rows show the type of the left
argument and the columns show the type of the right argument, in other words:
ResType = LeftArgument RelationalOperator RightArgument
From the table you can see that a valid use of the relational operators is, for
example Text compared with Text or Code, while Yes/No cannot be compared
with anything else than Yes/No and so on.

1. All relational operators are binary infix operators, in other words, takes a left
and a right argument and are placed between the arguments.

AL Programmers Guide

25

Type Conversion in Expressions

Table 11: Valid uses of relational operators


Right Argument

Left
Argument

26

Relational
Operators

Yes/No

Option

Integer

Long
Integer

Decimal No.

Date

Time

Text

Code

Yes/No

Yes/No

Option

Yes/No

Yes/No

Yes/No

Yes/No

Integer

Yes/No

Yes/No

Yes/No

Yes/No

Long Integer

Yes/No

Yes/No

Yes/No

Yes/No

Decimal No.

Yes/No

Yes/No

Yes/No

Yes/No

Date

Yes/No

Time

Yes/No

Text

Yes/No

Yes/No

Code

Yes/No

Yes/No

AL Programmers Guide

Type Conversion in Expressions

Yes/No (Logical) Operators


The logical operators can only be used with arguments which evaluates to the
type Yes/No.

Table 12: Yes/No operators


Operator:

Name:

Expression:

Resulting
Datatype:

NOT

Logical negation

NOT Yes/No

Yes /No

AND

Logical and

Yes/No AND Yes/


No

Yes/No

OR

Logical or

Yes/No OR Yes/No

Yes/No

As table 12 shows, the NOT operator is a unary prefix operator, that is, takes only
one argument and is placed in front of the argument, while the AND and OR
operators are binary infix operators, that is takes two arguments and are placed
between the corresponding arguments.

Introduction to Arithmetic Operators


In order to introduce the arithmetic operators, we start this section by showing
some examples, which exemplifies the function of the operators, and illustrate
the implicit type conversion automatically made by the AL compiler. The
examples has been divided into groups corresponding to each of the datatypes in
AL.
It is important to stress that the examples by no means are exhaustive. For a full
description of the type conversion rules in AL please refer to the tables in the
sections The Unary Arithmetic Operators and The Binary Arithmetic
Operators on page 31. These provide a full description of all possible uses of AL
operators and the resulting datatypes.
Examples: Option Operators

Table 13: Option operators


Operator:

Name:

Expression:

Resulting
Datatype

Addition

Option + Integer

Integer

Addition

Integer + Option

Integer

Subtraction

Option - Integer

Integer

Subtraction

Integer - Option

Integer

AL Programmers Guide

27

Type Conversion in Expressions

Examples: Integer Operators

Table 14: Integer operator examples


Operator:

Name:

Expression:

Resulting
Datatype

Unary plus

+ Integer

Integer

Unary minus

- Integer

Integer

Addition

Integer + Integer

Long Integer

Subtraction

Integer - Integer

Long Integer

Multiplication

Integer * Integer

Long Integer

Division

Integer / Integer

Decimal No.

DIV

Integer division

Integer DIV Integer

Integer

MOD

Modulus

Integer MOD Integer

Integer

Examples: Long Integer Operators

Table 15: Long Integer operator examples


Operator:

Name:

Expression:

Resulting
Datatype

Unary plus

+ Long Integer

Long Integer

Unary minus

-Long Integer

Long Integer

Addition

Long Integer + Long Integer

Long Integer

Subtraction

Long Integer - Long Integer

Long Integer

Multiplication

Long Integer * Long Integer

Long Integer

Division

Long Integer / Long Integer

Decimal No.

DIV

Integer division

Long Integer DIV Long Integer

Long Integer

MOD

Modulus

Long Integer MOD Long Integer

Long Integer

Examples: Decimal No. Operators

28

AL Programmers Guide

Type Conversion in Expressions

Table 16: Decimal No. operator examples


Operator:

Name:

Expression:

Resulting
Datatype

Unary plus

+ Decimal No.

Decimal No.

Unary minus

- Decimal No.

Decimal No.

Addition

Decimal No. + Decimal No.

Decimal No.

Subtraction

Decimal No. - Decimal No.

Decimal No.

Multiplication

Decimal No. * Decimal No.

Decimal No.

Division

Decimal No. / Decimal No.

Decimal No.

DIV

Integer Division

Decimal No. DIV Decimal No.

Decimal No.

MOD

Modulus

Decimal No. MOD Decimal No.

Decimal No.

Examples: Date Operators

Table 17: Date operator examples


Operator:

Name

Expression:

Resulting
Datatype

Date addition

Date + Number

Date

Date subtraction

Date - Number

Date

Date difference

Date - Date

Integer

In the 'Date addition' and 'Date subtraction' examples above, a run-time error will
occur if Date is a closing date. If date is undefined (0D), a run-time error will
occur.
Examples: Time Operators

Table 18: Time operator examples


Operator:

Name:

Expression:

Resulting
Datatype

Time addition

Time + Integer

Time

Time addition

Time + Long Integer

Time

AL Programmers Guide

29

Type Conversion in Expressions

Table 18: Time operator examples


Operator:

Name:

Expression:

Resulting
Datatype

Time subtraction

Time - Long Integer

Time

Time difference

Time - Time

Long Integer

The time unit is milliseconds. If time is undefined (0T), a run-time error will
occur.
Examples: Text and Code (String) Operators

Table 19: Text and Code operator examples


Operator:

Name:

Expression:

Resulting
Datatype

Concatenation

Text + Text

Text

Concatenation

Text + Code

Text

Concatenation

Code + Text

Text

Concatenation

Code + Code

Code

This ends the examples of the arithmetic operators. At this point you should feel
quite comfortable with the basic uses of the arithmetic operators in AL.
Nevertheless the above examples do not cover all the possible uses of the
operators. The following tables provide a full description of uses of the
arithmetic operators and the resulting datatypes.

The Unary Arithmetic Operators


The unary arithmetic operators in AL are so-called prefix operator, in other
words the syntax is:
PrefixExpression = Prefix Operator Expression
Table 20 shows for which datatypes the unary operators in AL are defined, and
the resulting datatypes returned by the system.
Invalid uses of the unary operators are indicated in the table by a dash. The table
shows that for instance the expression '- Integer' is valid, and evaluates to Integer,
while an expression like '+Yes/No' is invalid.

Table 20: Validity of unary operators


Unary Operator

30

AL Programmers Guide

Integer

Long Integer

Decimal No.

Type Conversion in Expressions

Table 20: Validity of unary operators


+

Integer

Long Integer

Decimal No.

Integer

Long Integer

Decimal No.

The Binary Arithmetic Operators


The binary arithmetic operators in AL are so-called infix operators, that is of the
following type:
InfixExpression = LeftExpression InfixOperator RightExpression
Table 21 shows for which datatypes the binary arithmetic operators are defined.
In the table the following signs are used:
Yes

Yes, the operator can take at least one operand (left,


right or both) of the given type.

No, the operator cannot be used with the given type.

Table 21: Binary arithmetic operators

Defined?

Yes/No

Option

Integer

Long
Integer

Decimal
No.

Date

Time

Text

Code

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

DIV

Yes

Yes

Yes

MOD

Yes

Yes

Yes

Each of the following tables define the valid uses of the binary arithmetic
operators, and the resulting datatypes.

AL Programmers Guide

31

Type Conversion in Expressions

The '+' Operator

Table 22: Resulting data type from the + operator


Right Argument
Yes/
No

Option

Integer

Long
Integer

Decimal
No.

Date

Time

Text

Code

Yes/No

Option

Long
Integer

Long
Integer(C)

Decimal
No. (C)

Integer

Long
Integer

Long
Integer

Long
Integer(C)

Decimal
No. (C)

Long Integer

Long
Integer(C)

Long
Integer

Long
Integer(C)

Decimal
No. (C)

Decimal
No.

Decimal
No.(C)

Decimal
No. (C)

Decimal
No. (C)

Decimal
No. (C)

Date

Date(A)

Date(A)

Date(A) (C)

(C)

(C)

(D)

Time

Time(B)

Time(B)

Time(B) (C)
(D)

Text

Text

Text

Code

Text

Code

Left
Argument

(C)

(C)

(C)

(A) in the above table means that the operation is not defined for the date 0D.
(B) in the above table means that the operation is not defined for the time 0T.
(C) in the above table indicates that overflow may occur.
(D) in the above table indicates that the operation is not defined if Decimal No.
has a fractional part.

32

AL Programmers Guide

Type Conversion in Expressions

The '-' Operator

Table 23: Resulting data type from the - operator


Right Argument

Yes/No

Option

Integer

Long
Integer

Decimal
No.

Date

Time

Text

Code

Yes/No

Option

Long
Integer

Decimal
No. (C)

Decimal
No. (C)

Decimal
No. (C)

Decimal
No. (C)

Decimal
No. (C)

Decimal
No. (C)

Decimal
No. (C)

Date(A)

Date(A)

Date(A) (C)

(C)

(C)

(D)

Integer(A)

Time(B)

Time(B)

Time(B) (C)

Integer

Left
Argument

Long Integer

Long
Integer

Decimal No.

Decimal
No. (C)

Date

Long Integer (C)

Long
Integer
Long
Integer
(C)

Time

Text

Code

(C)

Long
Integer
(C)

Long
Integer
(C)

(C)

(D)

Long
Integer(B)

(C)

(A) in the above table means that the operation is not defined for the date 0D.
(B) in the above table means that the operation is not defined for the time 0T.
(C) in the above table means that overflow may occur.
(D) in the above table indicates that the operation is not defined if Decimal No.
has a fractional part.

AL Programmers Guide

33

Type Conversion in Expressions

The * Operator
.

Table 24: Resulting data type from the *


operator
Right Argument

Left
Argument

Integer

Long Integer

Decimal
No.
Decimal
No. (C)

Integer

Long Integer

Long Integer

(C)

(C)

Long
Integer

Long Integer

Long Integer

(C)

(C)

Decimal
No. (C)

Decimal
No.

Decimal
No. (C)

Decimal
No. (C)

Decimal
No. (C)

The / Operator

Table 25: Resulting data type from the /


operator
Right Argument

Integer

Long
Integer

Decimal
No.

Integer

Decimal
No.

Decimal
No.

Decimal
No.

Long Integer

Decimal
No.

Decimal
No.

Decimal
No.

Decimal
No.

Decimal
No.

Decimal
No.

Decimal
No.

Left
Argument

A run-time error will occur if the right operand is equal to zero (0).

34

AL Programmers Guide

Type Conversion in Expressions

The 'MOD' and 'DIV' Operators

Table 26: Resulting data type from the MOD and


DIV operators
Right Argument

Left
Argument

MOD and DIV

Integer

Long Integer

Decimal No.

Integer

Integer

Long Integer

Decimal No.

Long Integer

Long Integer

Long Integer

Decimal No.

Decimal No.

Decimal No.

Decimal No.

Decimal No.

A run-time error will occur if the right operand is equal to zero (0).

AL Programmers Guide

35

Type Conversion in Expressions

36

AL Programmers Guide

Part

2 AL Reference Guide
The heading to the left
describes the name of the
function
A description of the argument(s) used in the function.
For each argument the Flow
is described. The Flow can
be either in (i), out (o) or io
(in/out), describing whether
the argument is used as
input, output or both. Furthermore the valid data
types are defined

callBATCH

Use this function to load and run a batch job. For example, you can
use this function to call a batch job that adjusts inventory

callBATCH(BatchName [, Request])

This document provides a detailed overview of the functions in the AppliBatchName


- Flow: i; data types: String
cation Language (AL). The functions
are ordered alphabetically and the
Enter the
name as
of the
you want tofor
run.AL
To choose
from the list
aim is that this document should
serve
a batch
reference
programof batch jobs, press F5 while in the AL editor. The system displays
mers.
the Symbols list. Highlight CallBatch and press F6
For each AL function,
the functioning,
the syntax and the arguments are
Request
-Flow: i; data types: Yes/No
explained. Furthermore there will often be general comments on the use
Use this parameter to determine whether or not the request panel
of the function, and all entriesappears
willbefore
be illustrated
ALjob.
code
examples.
the system runs by
the batch
The request
panel lets
user select
the setin
of information
other options.
The figure below explains thethelayout
used
the rest toofinclude
thisand
document.

This table tells you how to


accomplish some common
tasks

To...

Enter...

Display the request panel


before the batch job starts

TRUE or leave blank


(default)

Start the batch job without


displaying the request
panel

FALSE

Note: If the batch job does not exist, the system displays a
message when it executes this function

A thick vertical bar will be


used to draw your attention
to notes on special cases and
potential errors that you
should be aware of.

If you display the request panel, please note that the user has to
press ESC after the batch job runs to exit the request panel and start
the next AL function.

For Example ...

These examples show how to call a batch job. The first example
shows the request panel. The second example skips the request
panel and starts the batch job immediately.

With a request panel

All AL functions are exemplified by code samples

Name := Adjust Item Prices;


callBATCH(Name);

Sets the name of the


batch
Displays the request panel and
then, after the user selects
Start, runs the batch job

Without a request panel


Name := Adjust Item Prices;
callBATCH(Name, FALSE);

Sets the name of the


batch
Runs the batch job

AL Reference Guide

37

Typographical Conventions

Typographical
Conventions

This document uses special typefaces to help you distinguish between


various types of text.
Table 27 illustrates these conventions:

Table 27: Conventions

38

Convention

Applies to

Examples

Monospaced Font

AL code examples

Number:= 6;

Times Font

Normal text

This is a sentence

Helvetica Font

Table bodies

Text in a table cell

ALL CAPS

AL keywords when
used in normal text and
in AL code examples

...by using the MESSAGE function...

ALReference Guide

AL Functions grouped by category

AL Functions
grouped by
category

This section presents the AL functions grouped by category. The


functions are divided into the following 9 main categories:
1. String functions
2. Date and Time functions
3. Number functions
4. Database functions
5. Dialogue functions
6. Accounting functions
7. Array functions
8. Miscellaneous functions
9. Call functions

Some of the above groups are divided further into sub-groups. The
functions are described in alphabetical order from page 44.

String Functions
Maintenance
INSSTR

Inserts a string into another string.

DELSTR

Deletes a substring in a string.

DELCHR

Deletes one or more characters in a string.

COPYSTR

Copies (a part of) a string.

SETSTRLEN

Adjusts the length of a string.

STRSUBSTNO

Formats values into a string.

INCSTR

Increases or decreases a number in a string


with 1 (one).

SELECTSTR

Selects a sub-string in a comma saparated


string.

Information
STRLEN

Returns the length of a string.

MAXSTRLEN

Returns the maximum (defined) length for a


string variable.

STRPOS

Returns the position for a sub-string in a


string.

Transformation
UPPERCASE

Converts a string to uppercase.

LOWERCASE

Converts a string to lowercase.

AL Reference Guide

39

AL Functions grouped by category

CONVERTSTR

Converts the characters in a string.

STRCHECKSUM

Calculates a checksum for a numeric string.

Date and Time Functions


TIME

Returns the current system time.

TODAY

Returns the date from the operating system.

WORKDATE

The NAVISION work date.

Transformation
NORMALDATE

Returns the normal date for a date.

CLOSINGDATE

Returns the closing date for a date.

CALCDATE

Evaluates a date-expression.

DATE2DMY

Returns day, month or year for a date.

DATE2DWY

Returns weekday, weekno. or year for a date.

DMY2DATE

Returns the date based on day, month and


year.

DWY2DATE

Returns the date based on weekday, week


and year.

Number Functions
ABS

Calculates the absolute value for a number.

ROUND

Rounds off a number.

POWER

Calculates xy

Database Functions
Maintenance
dbINSREC

Inserts a record.

dbDELREC

Deletes a record.

dbDELALL

Deletes all records within a specified range.

dbMODIFYREC

Edits a record.

dbMODIFYALL

Edits a field in all records.

dbRECMARK

Marks a record.

Searching

40

ALReference Guide

AL Functions grouped by category

dbGETREC

Finds a record based on the current key and


filters.

dbFINDREC

Finds a record based on the current key and


filters.

dbNEXTREC

Find the next or previous record, based on


the specified record.

dbSETCURREC

Marks a record as current record.

dbGETCURREC

Retrieves a record, marked as current, from


the Object Stack.

dbRECCOUNT

The number of records in a table within a


specified range.

Transactions
dbLOCKTABLE

Locks a table in order to prevent conflicting


write transactions

dbCONSISTENT

Specifies whether a table is consistent.

dbCOMMIT

Commits (approves) a write transaction.

Keys and filters


dbSELECTKEY

Selects a current key for a table.

dbSETRANGE

Assigns a simple filter (single range) to a


field.

dbSETFILTER

Assigns a filter to a field.

dbMINRANGE

Finds the minimum value for a range.

dbMAXRANGE

Finds the maximum value for a range.

dbCOPYFILTER

Copies a filter from one field to another.

dbCOPYALLFILTERS

Copies all filters and marks from one record


to another record.

dbGETFILTER

Returns the filter for a field.

dbGETALLFILTERS

Returns all filters for a record.

dbMARKEDONLY

Delimits to records which have been marked.

dbRESETTABLE

Removes all filters for a table and selects the


primary key.

dbGLOBALFILTER

Makes all filters assigned to a table global.

dbSELECTCOMPANY

Redirects references to table data to another


company.

Fields
dbFIELDNO

Returns the field number for a field.

AL Reference Guide

41

AL Functions grouped by category

dbFIELDNAME

Returns the field name for a field.

dbFIELDACTIVE

Indicates if a field is active.

dbCALLFIELDCODE

Calls Entry Processing code for a field.

dbFIELDERROR

Creates an error message concerning a field.

dbTESTFIELD

Tests the contents of a field.

dbTRANSFERFIELDS

Copies fields from one record to another.

dbSUM

Sums specified columns in a table.

dbCALCFIELDS

Calculates the contents in calculated fields.

dbINITREC

Initializes the fields in a record.

Dialogue Functions
OPENWINDOW

Opens a window.

CLOSEWINDOW

Closes a window.

UPDATEWINDOW

Updates a field in a window with a value.

WINDOWINPUT

Allows keying in a field in a window.

ERROR

Prints a message and ends the AL code.

MESSAGE

Prints a message when the execution of the


AL code stops to wait for user interaction or
is ended.

CONFIRM

Creates a dialog box and prompts for a Yes/


No answer.

STRMENU

Creates a menu and waits for a selection.

WAITKEYPRESS

Waits for a key to be pressed.

Accounting Functions
COMPANYNAME

Returns the current Company name.

USERID

Returns the current user ID.

Array Functions

42

COPYARRAY

Copies a part of a one-dimensional array.

ARRAYLEN

Returns the total number of elements in an


array, or the number of elements in a specific
dimension.

COMPRESSARRAY

Removes empty strings in a text-array.

ALReference Guide

AL Functions grouped by category

Miscellaneous Functions
EVALUATE

Evaluates a text into a value.

FORMAT

Formats a value to a string.

CLEAR

Clears an internal variable.

CLEARALL

Clears all internal variables, keys and filters.

SERIALNO

Returns the serial number for your license


file.

SYSTEMVERSION

Returns the name and version of the


operating system.

Call Functions
callWINDOW

Loads and executes a window object.

callFUNCTION

Loads and executes a function object.

callMENU

Loads and executes a menu object.

callREPORT

Loads and executes a report object.

callBATCH

Loads and executes a batch object.

callIMPORT

Loads and executes an import object.

callSYSTEM

Executes a command in the operating


system.

AL Reference Guide

43

ABS

ABS

Calculates the absolute value of a Number, that is, ABS always returns a
positive numeric value or zero.

NewNumber := ABS(Number)
Number

- Flow: i; Data types: Number

The input value.


NewNumber

- Flow: o; Data types: Decimal No.

NewNumber 0.

For Example...

This example shows how to remove the sign from a negative numeric
value.
x := -10.235; {x is assigned a negative value}
y := ABS(x); {y is assigned the value of x without sign}
MESSAGE('x = %1, y = %2', x, y);

The message box will show:


x = -10.235, y = 10.235

44

ALReference Guide

ARRAYLEN

ARRAYLEN

Returns the total number of elements in an Array, or the number of elements in a specific Dimension.

Length := ARRAYLEN(Array [, Dimension])


Array

-Flow: i; Data types: Array

The array to be investigated.


Note: A run time error will occur if ARRAYLEN is used with
an input parameter which is not an array.

Dimension

-Flow: i; Data types: Number

If this optional argument is not used, the function will return the total
number of elements in the Array. In order to get the number of elements
in a specific dimension use Dimension with a valid value.
The valid values for Dimension are determined by the number of dimensions of the input array, for example the valid values for a three- dimensional array would be 1, 2 and 3.
Length

-Flow: o; Data types: Integer

The number of elements in the array, or the number of elements in a specific dimension.

For Example...

The following AL code sample shows how to use the optional arguments.
{Array1 is a one-dimensional integer array with size 2}
{Array2 is a two-dimensional integer array with size 2x2}
MESSAGE('Array1, Total number of elements %1', ARRAYLEN(Array1));
MESSAGE('Array2, Dimension no. 1: Size %1', ARRAYLEN(Array2,1));
MESSAGE('Array2, Dimension no. 2: Size %1', ARRAYLEN(Array2,2));
MESSAGE('Array2, Total number of elements: %1', ARRAYLEN(Array2));

The message boxes will show:


Array1, Total number of elements: 2

Array2, Dimension no. 1: Size 2

Array2, Dimension no. 2: Size 2

Array2, Total number of elements: 4

AL Reference Guide

45

CALCDATE

CALCDATE

Calculates a new date based on a DateExpression and a reference Date.

NewDate := CALCDATE(DateExpression [, Date])


DateExpression

-Flow: i; Data types: String

The date expression can be of any length. The system interprets the string
from left to right, one sub-expression at the time. The following rules
describes the valid syntax for a date expression:
DateExpression = <SubExpression>*
<SubExpression> = [<Sign>] <Term>
<Term> = <Number><Unit> | <Unit><Number>| <Prefix><Unit>
<Sign> = + | <Number> = Positive integer
<Unit> = D | WD | W | M | Q | Y
(D=day, WD=Weekday, W=Week, M=Month, Q=Quarter, Y=Year)
<Prefix> = C(C=Current)

The above (production-) rules express that date expressions consist of


zero or more sub-expressions. Each sub-expression is made of one of
three possible terms. Typical examples of terms are:
30D (30 days) corresponds to <Number><Unit>.
WD2 (weekday no. 2) corresponds to <Unit><Number>

Note: The internal calendar used in AL starts on a Monday


and ends on a Sunday. This means that Monday is weekday
no. 1 and Sunday is weekday no. 7.

CW (current week) corresponds to <Prefix><Unit>

Note: If the syntax of DateExpression is wrong, a run time


error will occur.

Date

- Flow: i; Data types: Date

This optional parameter can be used to define a reference date. The


default value is the current system date.
NewDate

- Flow: o; Data types: Date

The resulting date computed from the reference date and the date expression.

For Example...

The first example shows how to use the production rules above.
Prefix Unit Sign Number Unit Sign Number Unit
C
Q
+
1
M
10
D

This should be interpreted as: Current quarter + 1 month - 10 days.


The second example shows how to use the CALCDATE function.
Expr1 := 'CQ+1M-10D'; {Current quater + 1 month - 10 days}

46

ALReference Guide

CALCDATE

Expr2 := '-WD2';
{The previous Weekday no. 2, (last Tuesday)}
Expr3 := 'CM+30D';
{Current month + 30 days}
RefDate := 052196D;
Date1 := CALCDATE(Expr1, RefDate);
Date2 := CALCDATE(Expr2, RefDate);
Date3 := CALCDATE(Expr3, RefDate);
MESSAGE('The reference date is: %1 \' +
'The expression: %2 returns %3\' +
'The expression: %4 returns %5\' +
'The expression: %6 returns %7', RefDate, Expr1, Date1,
Expr2, Date2, Expr3, Date3);

The message box will show:


The
The
The
The

reference date is: 05/21/96


expression: CQ+1M-10D returns 07/20/96
expression: -WD2 returns 05/14/96
expression: CM+30D returns 06/30/96

AL Reference Guide

47

callBATCH

callBATCH

Loads and executes a batch object identified by a BatchName.

callBATCH(BatchName [, Request])
BatchName

- Flow: i; Data types: String

The name of the desired batch object. The string must contain an exact
match of the name of the report object (case sensitive). To choose from
the list of batch objects, press F5 while in the AL editor. The system displays the Symbols survey. Highlight the callBATCH keyword and press
F6.
Note: If the batch object BatchName does not exist, a run
time error will occur.

Request

- Flow: i; Data types: Yes/No

Determines whether the request window will be shown before the batch
object is executed.
To...

Enter...

Display the request window before


TRUE or leave blank (default)
the batch is started
Start the batch job without display
the request window

For Example...

FALSE

These examples show how to call a batch job. The first example shows
the request window. The second example skips the request window and
starts the batch job immediately.
With a request panel

Name := Adjust Item Prices;


callBATCH(Name);

{Sets the name of the batch}


{Displays the request window
and then, after the user
selects Start, runs the batch
job}

Without a request panel

Name := Adjust Item Prices;


callBATCH(Name, FALSE);

48

ALReference Guide

{Sets the name of the batch}


{Runs the batch job without
showing the request window}

callFUNCTION

callFUNCTION

Loads and executes a function object identified by a FunctionNo.

[Ok :=] callFUNCTION(FunctionNo [, Record])


FunctionNo

- Flow: i; Data types: Number

A number which identifies the desired function object. To choose from


the list of function objects, press F5 while in the AL editor. The system
displays the Symbols survey. Highlight the callFUNCTION keyword and
press F6.
Note: If the function object identified by FunctionNo does
not exist, a run time error will occur.

Record

- Flow: i; Data types: Record

Denotes a record to be used by the function identified by FunctionNo.


There are two types of function objects in the AL system.The first type is
associated to a specific table when it is created. When calling function
objects of this type, only records corresponding to the type of the associated table can be used. The second type of function object is not associated to a table, and when calling functions of this type, the optional
Record parameter cannot be used.
Note: If a function has been associated to a specific table, a
run time error will occur if the function is called with a record
from a different table.

Ok

- Flow: o; Data types: Yes/No

If the optional return value is not used, the system will terminate the execution of the AL code calling the function if an error occurs during the
execution of the function object.
If the return value is used, the system will continue the execution of the
calling AL code, although the function object detected an error, in other
words the programmer is supposed to take care of the error handling. The
possible values are described in the table below.
OK

Means that...

TRUE

No errors occured.

FALSE

An error occured during the execution of the function


object.

If the return value is used, the system will automatically clear the variables used in the function object, before the function object is executed.
If you use the return value in an IF statement, inside a write transaction, a
run time error will occur, unless your data updates are committed, before
you call callFUNCTION.

AL Reference Guide

49

callFUNCTION

General Comments...

When dbCOMMIT is used, all AL variables in Function objects currently


loaded, will be cleared. Consider the following example:

AL code module

...
callFUNCTION(1002);
callFUNCTION(1003);
dbCOMMIT();
callFUNCTION(1005);
...

When dbCOMMIT is executed, the AL variables used in the Function


objects 1002 and 1003 will be cleared by the system. The AL variables in
the Function object 1005 is not cleared, as the Function object has not
been loaded into memory at the point in time dbCOMMIT is executed.

For Example...

The following example shows how to use the callFUNCTION function.


Example 1:

A simple example, which shows how to call function no. 1001.


FunctionNo := 1001;
callFUNCTION(FunctionNo);
Example 2:

The Al code in this example uses a function numbered 1002, which calculates a unit price.
Function 1002 (Invoice Line)
Unit Price := Amount / Quantity;

Consider the following AL code sample:


IF callFUNCTION(1002,InvLine) THEN
MESSAGE(Unit Price is now calculated)
ELSE
ERROR(Quantity was zero);

By using the return value from callFUNCTION the programmer takes


care of the error handling. The following two code samples would cause a
run time error as the optional return value is not used:
callFUNCTION(1002,InvLine);

Stops code execution if Quantity = 0.


callFUNCTION(1002);

Works on local variable, will always stop code execution.


Example 3:

50

ALReference Guide

callFUNCTION

The AL code in this example uses a function numbered 1003, which


increases a counter variable.
Function 1003
Count := Count + 1;

Consider the following AL code sample:


FOR i := 1 TO 100 DO
callFUNCTION(1003);

If we assume that the value of count initially is zero, this loop will cause
count to be increased to 100. If the return value is used in a IF construct,
like:
FOR i := 1 TO 100 DO
IF callFUNCTION(1003) THEN
MESSAGE(Function 1003 called %1 times,i);

then Count will be cleared each time function 1003 is activated. This will
cause Count to take the values: 0->1, 0->1, 0->1 ...
Example 4:

The AL code in this example uses a function numbered 1004, which


selects a key, and assigns a filter to the Customer table.
Function 1004 (Customer)
dbSELECTKEY(Customer.No.);
dbSETFILTER(Customer.No., <1000);

Consider the following AL code sample:


dbSELECTKEY(Customer.Name);
dbSETFILTER(Customer.No., >=2000);
callFUNCTION(1004,Customer);

The call to the function 1004, does not change current key and filters. The
changes made in the function are local to the function.
Example 5:

Consider the following write transaction:


...
dbINSREC(Customer);
IF callFUNCTION(1005) THEN
...

The above call to function 1005 results in a run time error, as the write
transaction has not been committed. In order to call the function, the data
update must be explicitly committed:
...
dbINSREC(Customer);
dbCOMMIT();
IF callFUNCTION(1005) THEN
...

AL Reference Guide

51

callIMPORT

callIMPORT

Loads and executes an import object identified by an ImportName.

callIMPORT(ImportName [, Request])
ImportName

- Flow: i; Data types: String

The name of the desired import object. The string must contain an exact
match of the name of the report object (case sensitive). To choose from
the list of import objects, press F5 while in the AL editor. The system displays the Symbols survey. Highlight the callIMPORT keyword and press
F6.
Note: If the function is called with the name of a nonexisting import object, a run time error will occur.

Request

- Flow: i; Data types: Yes/No

Determines whether the request window will be shown before the importtransaction is started.
To...

Enter...

Display the request window before


TRUE or leave blank (default)
the import transaction is started
Start the import transaction without
FALSE
showing the request window

For Example...

These examples show how to start an import transaction. The first example shows the request window. The second example skips the request window and starts the import transaction immediately.
With a request window

Name := Consolidation - Database;


callIMPORT(Name);

{The import transaction}


{Displays the request window
and then, after the user
selects Start, executes the
import transaction}

Without a request window

Name := Consolidation - Database;


callIMPORT(Name, FALSE);

52

ALReference Guide

{The import transaction}


{Executes the import transaction}

callMENU

callMENU

Loads and executes a menu object identified by a MenuNo.

callMENU(MenuNo)
MenuNo

- Flow: i; Data types: Number

A number which identifies the menu object. To choose from the list of
menu objects, press F5 while in the AL editor. The system displays the
Symbols survey. Highlight the callMENU keyword and press F6.
Note: If MenuNo does not exist, a run time error will occur.

For Example ...

This AL code sample shows how to activate a menu.


MenuNo := 2;
callMENU(MenuNo);

{The menu to be displayed}


{Activate the menu}

AL Reference Guide

53

callREPORT

callREPORT

Loads and executes a report object identified by a ReportName.

callREPORT(ReportName [, Request])
ReportName

- Flow: i; Data types: String

The name of the desired report object. The string must contain an exact
match of the name of the report object (case sensitive). To choose from
the list of report objects, press F5 while in the AL editor. The system displays the Symbols survey. Highlight the callREPORT keyword and press
F6
Note: If the report object identified by ReportName does not
exist, a run time error will occur.

Request

- Flow: i; Data types: Yes/No

Determines whether the request window will be shown before the report
is started.
To...

Enter...

Display the request window before


TRUE or leave blank (default)
the report is started
To start the report directly

For Example...

FALSE

These example show how to execute a report. The first example displays
the request window. The second example skips the request window and
starts the report immediately. Please note that the name string is case sensitive, and must contain an exact match of the report object name.
With a request window

ReportName := 'BOM Raw Materials';


callREPORT(ReportName);

{The report name}


{Displays the request window
and then, after the user
selects start, executes the
report}

Without a request window

ReportName := 'BOM Raw Materials';


callREPORT(ReportName, FALSE);

54

ALReference Guide

{The report name}


{Executes the report}

callSYSTEM

callSYSTEM

Makes it possible to execute external programs and operating system


commands from AL programs.

ReturnCode := callSYSTEM(Name [, Param, ...]


Name

- Flow: i; Data types: String

The path and name of the command.


Param, ...

- Flow: i; Data types: String

One or more optional argument(s) to be sent to the operating system command. Each parameter is treated as a substring, that is all parameters are
concatenated into one string which forms the argument to the operating
system command. This means that if more than one parameter is to be
passed to the operating system command, the parameters can be supplied
either as individual arguments, or as a string in which the arguments are
separated by spaces. The total length of the string(s) cannot exceed 128
characters.
ReturnCode

-Flow: o; Data types: Integer

This is a return code from the external program or the operating system
command.
External programs can return different codes in order to reflect whether
one or more error(s) occured during the execution of the external program. The possible value of the ReturnCode will depend on the external
program.
When using the callSYSTEM command to activate operating system
command, the following simple rules apply. DOS commands (DIR,
COPY, ...), OS/2 commands (DIR, COPY, ...) and UNIX commands (ls,
cd, ...) will always return the value 0 (zero).

General Comments...
For Example...

When callSYSTEM returns, the screen is automatically updated.


The general format of a callSYSTEM statement is:
callSYSTEM('c:\joe\myprog.exe', 'arg1', 'arg2');

This executes a program named 'myprog.exe' in the directory 'joe' at drive


c:. 'arg1' and 'arg2' denotes arguments which are passed to the function.
To call the DOS command DIR, execute the following:
CommandProcessor := 'c:\command.com';
{The path and name of the DOS command processor.}
Argument := '/c';
OsCommand := 'dir';
MESSAGE('Press ENTER to execute the DOS command %1', OsCommand);
WAITKEYPRESS();
callSYSTEM(CommandProcessor, Argument, OsCommand);

In order to execute the corresponding OS/2 command, substitute the com-

AL Reference Guide

55

callSYSTEM

mand processor 'command.com' with 'cmd.exe'.


In order to execute the corresponding UNIX command 'ls', it is not necessary to activate a command processor. The following statement is sufficient to get a list of the files in the current directory:
callSYSTEM('ls');

56

ALReference Guide

callWINDOW

callWINDOW

Loads and executes a window object identified by a WindowNo. All window objects are associated with a specific table and when a window
object is executed, one or more records (depending on the type of the window) from this table will be displayed .

Ok:= callWINDOW(WindowNo [, Record] [, Record.Field])


WindowNo

- Flow: i; Data types: Number

A number which identifies the window object.


If callWINDOW is used with WindowNo = 0, the default lookup window
for the actual table will be shown. In this case the optional parameter
Record must be used, in order to specify the table.
To choose from the list of window objects, press F5 while in the AL editor. The system displays the Symbols survey. Highlight the callWINDOW
keyword and press F6
Note: If the window object identified by WindowNo does not
exist, a run time error will occur.

Record

- Flow: i; Data types: Record

The callWINDOW function will as default show the record which was
displayed the last time the window was open. For each window object the
system stores information about the most recently shown record and the
attached key and filters.
This optional parameter is used to select a specific record to be shown in
the window. The record must be of the same type as the table associated
to the window. When showing the record, the window will use the key
and filters attached to the record.
Record.Field

- Flow: i; Data types: RecordField

Is used to position the cursor on a specific field. If this optional parameter


is not used, the default cursor position will be either in the upper left of
the screen, or the same position as the last time the window was open..
Note: When a List, Tabular, Chart or Card window object has
been called with the optional parameter Record, it is possible
to assign values to the fields in the record. This is done by
placing the cursor on the primary key field in an arbitrary
record. When ENTER is pressed the system returns to the AL
code and the values in the record pointed out by the cursor
has now been assigned to the fields in Record.

Ok

- Flow: o; Data types: Yes/No

For the window objects: List, Tabular, Chart and Card this return code
reflects whether the ENTER or ESC was used to quit the window.

AL Reference Guide

57

callWINDOW

.
Ok

Means that...

TRUE

ENTER was used to quit the window. This also selects the
record pointed out by the cursor.

FALSE

ESC was used to quit the window.

The only way to quit the other window objects (Worksheet, Matrix, Statistics and Periodic) is by using ESC, that is, they will always return the
value FALSE.

For Example...

The following examples illustrates how to use the callWINDOW function.


Example 1:

A simple example, which shows how to call window no. 1001.


WindowNo := 1001;
callWINDOW(WindowNo);

{A window object}
{Activate the window object
with default values for Record
and Record.Field}

Example 2:

Consider the following AL code sample:


dbSELECTKEY(Customer.Name);
dbSETFILTER(Customer.No., >=2000);
callWINDOW(1002,Customer);

The window will show customers with no. greater than or equal to
2000, sorted by Name. The current key and filters on Customer are never
changed when using callWINDOW, as the user operates on a copy of the
filters and the current key.
Example 3:

The following construct can be used to detect whether the user pressed
ESC or ENTER in the window.
IF callWINDOW(1002,Customer) THEN
MESSAGE(ENTER was pressed - Customer has changed)
ELSE
MESSAGE(ESC was pressed - Customer is unchanged);

If ENTER was pressed, callWINDOW returns the Customer highlighted


in the window.
Example 4:

The AL code sample:


callWINDOW(1002);

will show the same list of customers, use the same sorting sequence, and
show the same current customer as last time the window was open. All
filters and the current key are fetched from a setup-file.

58

ALReference Guide

CLEAR

CLEAR

Clears the value of a Variable.

CLEAR(Variable)
Variable

- Flow: i; Data types: -

This parameter is an identifier (variable) of any AL datatype (both simple


and composite datatypes).

Number variable will be set to the value 0.

A String variable will be set to the empty string ''.

A Date variable will be set to the undefined date 0D.

A Time variable will be set to the undefined time 0T.

A Yes/No variable will be set to the value FALSE.

For a composite data type, such as a record or an array all elements will
be cleared.

For Example...

This example shows how to use CLEAR.


Name := 'Joe Blow';
MESSAGE('Initially the variable "Name" contains: >%1<', Name);
CLEAR(Name);
MESSAGE('After using CLEAR the variable "Name" contains: >%1<',
Name);

The first message box will show:


Initially the variable Name contains: >Joe Blow<

...while the second message box will show:


After using CLEAR the variable Name contains: ><

AL Reference Guide

59

CLEARALL

CLEARALL

Clears all internal variables, keys and filters in the object (function,
report, ...) containing the AL code.

CLEARALL()
General Comments...

When a function is called repeatedly within the same transaction, all values for variables and filters are retained in memory between the calls
(This is for example used to enumerate entry numbers when booking) .
When you do not need the values retained in memory, you can use the
CLEARALL() function in order to ensure that all variable are cleared.
Refer to the description of the CLEAR function for a description of initial
values in cleared variables.

For Example...

60

This function is for example used in function no. 80 (Sales-post) in the


NAVISION demo application.

ALReference Guide

CLOSEWINDOW

CLOSEWINDOW

Use this function to close a dialog window which has been opened by
OPENWINDOW.

CLOSEWINDOW()
General Comments...

Observe the following:


If OPENWINDOW has not been called, a run time error will occur.
Each object (function-, report-, ...) can only have one dialog window
open at a time. The system will automatically close an existing dialog
window before it opens a new dialog window.
The system will also close the dialog window automatically when an
object is terminated.

For Example...

This example shows how to use the CLOSEWINDOW function.


OPENWINDOW('This is a window\Sample text no. 1: #1#####\'+
'Sample text no. 2: #2#########');
{Defines a window with 2 fields}
UPDATEWINDOW(1, 'Hello');
{Inserts text in field 1}
UPDATEWINDOW(2, 'out there'); {Inserts text in field 2}
WAITKEYPRESS();
{Pauses until ESC or ENTER is
pressed}
CLOSEWINDOW();
{Close the window}

The window will show:


This is a window
Sample text no. 1: Hello
Sample text no. 2: out there

When you press ESC or ENTER the window will be removed by the
CLOSEWINDOW function.

AL Reference Guide

61

CLOSINGDATE

CLOSINGDATE

Returns the closing date for a Date.

ClosingDate := CLOSINGDATE(Date)
Date

- Flow: i; Data types: Date

The input date.


Note: All dates have a corresponding closing date. A closing
date is regarded by the system as a period following the given
date, but before the next normal date, thus closing dates are
sorted immediately after the corresponding normal date, but
before the next normal date.

The figure below illustrates how the system sorts the closing dates
between the normal dates for an arbitrary month..
April 1994
040194D 040194C

040294D

040294C 040394D 040394C

...

xxxxxxD: Normal date


xxxxxxC: Closing date

ClosingDate

- Flow: o; Data types: Date

The corresponding closing date for Date.

For Example...

If Date is a...

Then ClosingDate will be a...

Normal date

Closing date

Closing date

Closing date

These examples show how to use the CLOSINGDATE function. In the


first example a normal date is given as input, and in the second example a
closing date is given as input.
A normal date as input.

Date1 := 040496D;
CloDate := CLOSINGDATE(Date1);
MESSAGE('The closing date for %1 is %2', Date1, CloDate);

The message box will show:


The closing date for 04/04/96 is C04/04/96
A closing date as input.

Date1 := CLOSINGDATE(040496D);
Clodate := CLOSINGDATE(Date1);
MESSAGE('The closing date for %1 is %2', Date1, CloDate);

The message box will show:


The closing date for C04/04/96 is C04/04/96

62

ALReference Guide

COMPANYNAME

COMPANYNAME

Returns the current company name.

Name := COMPANYNAME()
Name

- Flow: o; Data types: String

The company name, or the empty string if a company has not been
selected.

General Comments...

For Example...

When you enter your application, you will be prompted to select a company. The COMPANYNAME function reflects this selection
This example shows how to use the COMPANYNAME function to
retrieve the name of the current company.
Name := COMPANYNAME();
{Get the name of the current company}
MESSAGE('The company name is: %1', Name);
{Print the companyname}

AL Reference Guide

63

COMPRESSARRAY

all non-empty strings in an array of strings (StringArray), to the


COMPRESSARRAY Moves
start of the array. In other words the resulting StringArray has the same
number of elements as the input array, but empty entries and entries only
containing blanks appears at the end of the array. Refer to figure below.

COMPRESSARRAY(StringArray)
StringArray

- Flow: io; Data types: Array

The string type array to be compressed.


Note: When compressing an array of strings, the non-empty
strings in the resulting array will have the same sorting as in
the original array.

General Comments...

The COMPRESSARRY function is especially useful when printing


names and addresses, for instance in statements of accounts where blank
lines must be removed.

For Example...

The following figure illustrates the function of COMPRESSARRAY. The


figure shows a StringArray given as input to left, and the corresponding
output to the right.

COMPRESSARRAY
Input

Output

Joe Blow

Joe Blow

1241 East Druid Avenue

1241 East Druid Avenue

Chicago

Chicago

The figure illustrates that all non-empty entries have been moved to the
top of the array.
The following AL code sample illustrates how to perform the above compression of an array of strings.
Name[1] := ; {Empty String}
Name[2] := 'Joe Blow';
Name[3] := '1241 East Druid Avenue';
Name[4] := ' '; {A string containing blanks}
Name[5] := 'Chicago';
Name[6] := ;
MESSAGE('The address before compression, is written as:\' +
'%1\' +
'%2\' +

64

ALReference Guide

COMPRESSARRAY

'%3\' +
'%4\' +
%5\ +
%6, Name[1], Name[2], Name[3], Name[4], Name[5], Name[6]);
COMPRESSARRAY(Name); {The empty lines (strings) are removed}
MESSAGE('The address after compression, is written as:\' +
'%1\' +
'%2\' +
'%3\' +
'%4\' +
%5\ +
%6, Name[1], Name[2], Name[3], Name[4], Name[5], Name[6]);

The first message-box will show:


The address before compression, is written as:
Joe Blow
1241 East Druid Avenue
Chicago

The second message-box will show:


The address after compression, is written as:
Joe Blow
1241 East Druid Avenue
Chicago

The empty and blank strings which caused blank lines, has been moved to
the end of the array, and the other elements has been moved up.
Note that an empty string is not printed when it occurs as the last (or the
first) line in a message box.

AL Reference Guide

65

CONFIRM

CONFIRM

Creates a dialog box which prompts for a Yes/No answer. The dialog box
is centered at the middle of the screen.

Ok := CONFIRM(String [, Default])
String

- Flow: i; Data types: String

This string is displayed in the dialog box. Use the '\'character to indicate a
new line. The height of the dialog box corresponds to the number of lines,
and the width of the dialog box corresponds to the length of the longest
substring. A Yes/No prompt field must be inserted in the string by using
'#'-characters. Normally 3 '#'-characters are used, as this length matches
both 'Yes' and 'No'.
Note: A run time error will occur if String does not contain a
'#'-field.

Default

- Flow: i; Data types: Yes/No

Describes what the computer should use as default in the Yes/No field. If
the variable Default is not specified, the value 'No' will be used.
Ok

- Flow: o; Data types: Yes/No

The return parameter reflects the user's selection:

For Example...

Ok will be...

If you entered...

TRUE

Yes

FALSE

No

In the following example the CONFIRM function is used to prompt the


user for a Yes / No answer:
Question := 'Leave without saving changes? ###';
{A string containing a #-field for a Yes/No prompt}
Answer := CONFIRM(Question, TRUE);
MESSAGE(You selected %1, Answer);

66

ALReference Guide

CONVERTSTR

CONVERTSTR

Converts the characters in a String according to the characters in the two


strings FromCharacters and ToCharacters, which serves as conversion
tables.
Example: If FromCharacters[1] is found in String[x] then String[x] is set
to ToCharacters[1]. If FromCharacters[2] is found in String[y] then
String[y] is set to ToCharacters[2] and so on.
NewString := CONVERTSTR(String, FromCharacters, ToCharacters)

String

- Flow: i; Data types: String

The string to be converted.


FromCharacters

- Flow: i; Data types: String

A string describing which characters to be replaced. The CONVERTSTR


function is case-sensitive.
ToCharacters

- Flow: i; Data types: String

A string describing the new characters to be inserted. The CONVERTSTR function is case sensitive. The length of this string must correspond
to the length of FromCharacters. If the length of the FromCharacters and
ToCharacters strings both are 0, (empty strings) no changes are made.
Note: If the lengths of the FromCharacters and ToCharacters
strings are not equal, a run time error will occur.

NewString

- Flow: o; Data types: String

The converted string.

For Example...

The following shows how to use the CONVERTSTR function


OriginalString := 'Want to leave without saving?';
FromChars := 'lws';
ToChars := 'LWS';
NewString := CONVERTSTR(OriginalString, FromChars, ToChars);
MESSAGE('The original sentence is:\ %1', OriginalString);
MESSAGE('The sentence is converted to:\ %1', NewString);

The first message box will show:


The original sentence is:
Want to leave without saving?

...while the second message box will show:


The sentence is converted to:
Want to Leave Without Saving?

AL Reference Guide

67

COPYARRAY

COPYARRAY

Copies one or more elements from an Array to a NewArray.

NewArray := COPYARRAY(Array, Position [, Length])


Array

- Flow: i; Data types: Array

The array to be copied from. Copying can only take place from onedimensional arrays. Two- and three-dimensional arrays can be copied by
repeated use of the COPYARRAY function.
Position

- Flow: i; Data types: Number

The position of the first array-element to be copied.


Length

- Flow: i; Data types: Number

The number of array elements to be copied. Valid values for Length are:
1 Length MAXLEN(Array) - Position + 1
If Length is not specified, all array elements from Position to the last element will be copied.
NewArray

- Flow: o; Data types: Array

The resulting array is one-dimensional.

For Example...

The following two examples show how to use the COPYARRAY function. The first example shows how to copy three elements from a one
dimensional array. The second example shows how to copy two and three
dimensional arrays.
How to copy from a one-dimensional array.

{Old has been defined as a one-dimensional array with size 5, and


{New has been defined as a one-dimensional array with size 3}
Old[1] := 'Joe';
Old[2] := 'Blow';
Old[3] := 'lives';
Old[4] := 'in';
Old[5] := 'Chicago';
New := COPYARRAY(Old,3);
MESSAGE('The contents of the Old array is:\' +
'1: %1 \' +
'2: %2 \' +
'3: %3 \' +
'4: %4 \' +
'5: %5', Old[1], Old[2], Old[3], Old[4], Old[5]);
MESSAGE('The contents of the copied array is:\' +
'1: %1 \' +
'2: %2 \' +
'3: %3 \', New[1], New[2], New[3]);

68

ALReference Guide

COPYARRAY

The first message box will show the following:


The contents of the Old array is:
Joe
Blow
lives
in
Chicago

... while the second message box will show the following:
The contents of the copied array is:
lives
in
Chicago
How to copy from a two or three dimensional array.

It is possible to copy two and three dimensional array by repeated used of


the COPYARRAY function. This example shows one way to copy a
(small) two dimensional array by using two COPYARRAY statements.
{Assume that the Arr and New arrays both are 2x2 Integer arrays}
Arr[1,1] := 11;
Arr[1,2] := 12;
Arr[2,1] := 21;
Arr[2,2] := 22;
New[1] := COPYARRAY(Arr[1], 1);
New[2] := COPYARRAY(Arr[2], 1);
MESSAGE(The resulting array: \ +
(%1)
(%2) \ +
(%3)
(%4), New[1,1], New[1,2], New[2,1], New[2,2]);

The message box will show:


The resulting
array:
(11)
(21)

(12)
(22)

When using COPYARRAY to copy large two or three dimensional arrays


you should use one of the looping constructs available in AL.

AL Reference Guide

69

COPYSTR

COPYSTR

Copies a sub-string of any Length from a specific Position in a String to a


NewString.

NewString := COPYSTR(String, Position [, Length])


String

- Flow: i; Data types: String

The string to be copied from.


Position

- Flow: i; Data types: Number

The position of the first character to be copied. The value of Position


must be greater than 0. If Position is greater than STRLEN(String) then
an empty string will be returned by COPYSTR.
Length

- Flow: i; Data types: Number

The number of characters to be copied. Length must be greater than 0. If


the value of Length causes that:
Position + Length > STRLEN(String)
then the resulting string will be all characters from Position to the end of
the string.
If Length is not specified, the resulting string will be all characters from
Position to the end of the string.
NewString

- Flow: o; Data types: String

The resulting (sub-)string.

For Example...

This shows how to use the COPYSTR function.


Str := 'Using the COPYSTR function';
Position := 7;
Length := 8;
MESSAGE('The original string is:\>%1<', Str);
NewStr := COPYSTR(Str, Position, Length);
MESSAGE('The copied string is: \>%1<', NewStr);

The first message-box shows the original string:


The original string is:
>Using the COPYSTR function<

...while the second message-box will show the copied (sub-)string:


The copied string is:
>the COPY<

70

ALReference Guide

DATE2DMY

DATE2DMY

Returns the day, month and year based on a Date.

Number := DATE2DMY(Date, What)


Date

- Flow: i; Data types: Date

The input date.


What

- Flow: i; Data types: Number

Specifies what the function should return. The valid values are 1, 2 and 3:
What

Number

Corresponds to...

Day (1-31).

Month (1-12).

Year (1980 - 2059).

- Flow: o; Data types: Integer

The resulting output day, month or year.

For Example...

The following shows how to use the DATE2DMY function.


D := 120190D;
Day := DATE2DMY(D, 1);
{Get the
Month := DATE2DMY(D, 2); {Get the
Year := DATE2DMY(D, 3); {get the
MESSAGE('The date %1, corresponds
'Day no. %2 in\' +
'month no. %3 in\' +
'the year %4 \', D, Day,

date}
month no.}
year}
to:\' +

Month, Year);

The message box will show the following:


The date 12/01/90 corresponds to:
Day no. 1 in
month no. 12 in
the year 1990

AL Reference Guide

71

DATE2DWY

DATE2DWY

Returns the day of the week, week number and year based on the input
Date.

Number := DATE2DWY(Date, What)


Date

- Flow: i; Data types: Date

The input date.


What

- Flow: i; Data types: Number

Specifies what the function should return. The valid values are 1, 2 and 3:
What

Number

Corresponds to...

day of the week (1-7, Monday = 1).

week number (1-53).

year (1980 - 2059).

- Flow: o; Data types: Integer

The resulting day of the week, week number or year.

General Comments...

For Example...

A special situation occurs if the input date to the DATE2DWY function is


in a week which overlaps two years. The DATE2DWY function computes
the output year as the year of the first day in this week. This is illustrated
in the example below.
The following illustrates a special case which occures when using the
DATE2DWY function in a week which overlaps two years.
D := 010194D;
DayOfWeek := DATE2DWY(D, 1);
WeekNumber := DATE2DWY(D, 2);
Year := DATE2DWY(D, 3);
MESSAGE('The date %1, corresponds to:\' +
'The day of the week: %2\' +
'The week number: %3\' +
'The year: %4', D, DayOfWeek, WeekNumber, Year);

The message box will show:


The
The
The
The

date 01/01/94, corresponds to:


day of the week: 6
week number: 52
year: 1993

The example illustrates that the system regards the date 01/01/94 as day
no. 6 (Saturday) in week no. 52 in the year 1993!

72

ALReference Guide

dbCALCFIELDS

dbCALCFIELDS

Updates Calculated fields in a record

dbCALCFIELDS(Record.Field [, Record.Field, ...])


Record.Field

-Flow: io; Datatypes: RecordField

Fields that are to be updated. Each field must be defined as a Calculated


field. All fields must belong yo the same record variable.

General Comments...

The Caluclated fields are of Yes/No or Decimal No. type. Calculated


fields are a special NAVISION feature which provides information about
other tables in the database in the same company. A Calculated field of
type Decimal No. holds sums accumulated from columns in other tables,
while Calculated fields of type Yes/No confirms whether records exist
within a specified range in another table.
Calculated fields are virtual fields, as their values are not saved with the
table. Calculated fields require that dbCALCFIELDS are called in order
to be updated.
For example, Calculated fields in records fetched with dbFINDREC and
dbNEXTREC are set to zero. A call to dbCALCFIELDS are needed to
update their values. A detailed explanation of calculated fields can be
found in The NAVISION DBMS.

For Example ...

The following AL code sample shows how to use the dbCALCFIELDS


function. We show how to find the balance at December 31, 1994 and the
movement for a customer in 1994:
dbSETRANGE(Customer.Date Filter,010194D,123194D);
dbCALCFIELDS(Customer.Balance, Customer.Movement);

The first line sets up a delimitation for the field Date Filter in the record
Customer. This field is a Calculation Filter field which is used in the
computation of some of the Calculated fields in Customer. The computation of the Calculated fields is then carried out using dbCALCFIELDS in
the second line.

AL Reference Guide

73

dbCALLFIELDCODE

dbCALLFIELDCODE

Calls the Entry Processing code for the field Rec.Field.

dbCALLFIELDCODE(Record.Field [, Value])
Record.Field

- Flow: io; Datatypes: RecordField

A field with associated Entry Processing code.


Value

Flow: i; Datatypes:-

The optional parameter Value is used to assign a value to the field before
the entry processing takes place. The type of Value must match the type of
Record.Field.

For Example ...

The following AL code sample illustrates the use of the dbCALLFIELDCODE function.
When an account no. is entered in a ledger, some entry processing code
must be activated in order to, for example, transfer the name of the
account from the chart of accounts.
If an account no. is entered in a batch, the code which transfers the name
of the account will not automatically be executed. The following causes
the appropriate code to be executed:
dbCALLFIELDCODE(GeneralLedgerLine.AccountNo, 100);

This corresponds to:


GeneralLedgerLine.AccountNo := 100;
dbCALLFIELDCODE(GeneralLedgerLine.AccountNo);

74

ALReference Guide

dbCOMMIT

dbCOMMIT

Confirms updates made to the database.

dbCOMMIT()
General Comments...

The dbCOMMIT() function is used to explicitly commit an update to the


database. When the system enters an AL code module it automatically
enables write transactions to be performed, and when the system exits the
AL code module it automatically ends the write transaction by committing the update(s) made by the AL code. This means that if the AL code
module only is to perform a single write transaction, then the update is
automatically committed. But if the AL code module is to perform several
write transactions, then dbCOMMIT() must be used to explicitly end one
write transaction, before the next can be started. In other words: the
dbCOMMIT function separates the write transactions in an AL code module.
See The NAVISION DBMS for a detailed discussion about committing
database updates.

For Example ...

Figure 1. Committing updates


in AL code

Figure 1 illustrates how to use the dbCOMMIT function. The AL code


contains two write transactions. As the execution of the AL code begins, a
write transaction is automatically started. By issuing the command
dbCOMMIT(), you tell the system that the first write transaction has ended,
and prepares the system for the second. As the execution of the AL code has
been completed, the system automatically ends the second write transaction.

BeginWriteTransaction
AL Module

AL Statements
dbCommit(...)

AL Statements

}
}

1. Trans.

2. Trans.

EndWriteTransaction

AL Reference Guide

75

dbCONSISTENT

dbCONSISTENT

Marks a table as being consistent or inconsistent according to an administrative point of view.

dbCONSISTENT(Record, Consistent)
Record

- Flow: i; Datatypes: Record

The table to marked.


Consistent

- Flow: i; Datatypes: Yes/No

A mark to be set on Table.

General Comments...

To...

Enter...

Mark the table as consistent.

TRUE

Mark the table as inconsistent.

FALSE

Normally this function is only used for accounting routines.


If your accounts do not balance, the accounts are inconsistent. dbCONSISTENT is used to assure that no inconsistent changes are made to your
accounts
If an attempt is made to commit a write transaction when a table is
marked as inconsistent, an error message will occur, and all updates made
in the write transaction is aborted.

For Example...

A typical example of inconsistency is if the sum of all the entries in a


table containing finance entries does not balance (that is, not zero).
Imagine that your application includes a function object: ChangeAmount.
This function is used to withdraw respectively put in amounts in the
FinanceEntry table in your application. When an amount is withdrawn
from an account, the system is inconsistent until a corresponding amount
is put in. The pseudo code below illustrates the ChangeAmount function.
FUNCTION ChangeAmount(DecimalNo.: Amount) BEGIN
GLEntry.G/L Account No. := 1000;
GLEntry.Amount := Amount;
dbINSREC(GLEntry);
Balance := Balance + Amount;
IF Balance = 0 THEN
Consistent := TRUE
ELSE
Consistent := FALSE;
dbCONSISTENT(GLEntry, Consistent);
END; {ChangeAmount}

The function uses a variable Balance to express the change in balance.


When the function is used to withdraw for example $100, the Balance
variable will reflect this as -$100. When the function is used to put in one

76

ALReference Guide

dbCONSISTENT

or more amounts which sum up to +$100, the Balance variable will equal
zero, and the table will be marked as consistent. This means that if an
attempt is made to put in an amount and end the write transaction (commit the change) without withdrawing a corresponding amount within the
same transaction, an error will occur, and the write transaction will be
aborted.

AL Reference Guide

77

dbCOPYALLFILTERS

dbCOPYALLFILTERS

Copies all filters set by dbSETFILTER/dbSETRANGE and the state of


dbMARKEDONLY from one record to another.

dbCOPYALLFILTERS(FromRecord, ToRecord)
FromRecord

- Flow: i; Datatypes: Record

The filters are copied from this record.


ToRecord

- Flow: o; Datatypes: Record

The filters are copied to this record.

General Comments...

For Example...

This function is used to apply filters defined for another record, as basis
for a counting, a search, calculation or similar operations.
This AL code sample shows how to use the dbCOPYALLFILTERS function.
dbSETFILTER(CustomerRec1.No., <1000);
{Set various}
dbSETRANGE(CustomerRec1.Group, 1);
{filters on fields}
dbMARKEDONLY(CustomerRec1, TRUE);
{in a Customer record}
dbCOPYALLFILTERS(CustomerRec1, CustomerRec2);{Apply the same}
.
{filters to another}
.
{record}
Count := dbRECCOUNT(CustomerRec2);

The filters defined for CustomerRec1 is copied, and applied to


CustomerRec2 and affects the result returned by the dbRECCOUNT
function in the last line.

78

ALReference Guide

dbCOPYFILTER

dbCOPYFILTER

Copies the filter set for one field and applies it to another field.
dbCOPYFILTER(FromRecord.FromField, ToRecord.ToField)

FromRecord.FromField

- Flow: i; Datatypes: RecordField

The filter set for this field is copied.


ToRecord.ToField

- Flow: o; Datatypes: RecordField

The filter copied from FormRecord.FromField is applied to this field.

General Comments...

For Example...

The FromFields and Tofields must be of the same data type, but does not
need to belong to the same table.
This AL code sample illustrates how to use the dbCOPYFILTER function
dbSETFILTER(Customer.No., <1000);
dbCOPYFILTER(Customer.No., Vendor.No.);
.
.
Count := dbRECCOUNT(Vendor);

The filter set for Customer.No. is copied and applied to Vendor.No..


This again affects the result of the dbRECCOUNT function, which counts
the number of vendors with a number less than 1000.

AL Reference Guide

79

dbDELALL

dbDELALL

Deletes all records within a specified range in a table.

dbDELALL(Record)
Record

- Flow: io; Datatypes: Record

Identifies the table in which the deletion will take place. Only records
within the range specified by the filters set for Record will be deleted.

For Example...

This AL code sample illustrates the function of the dbDELALL function.


WHILE dbFINDREC(Customer, -) DO
dbDELREC(Customer);

The above code performs the same operation as:


dbDELALL(Customer);

But the dbDELALL function is much faster, as only one access to the
server is needed, while the first method requires multiple accesses to be
performed.

80

ALReference Guide

dbDELREC

dbDELREC

Deletes a record in a table.

[Ok :=] dbDELREC(Record)


Record

-Flow: i; Datatypes: Record

The record to be deleted. Record itself does not change.


Ok

- Flow: o; Data types: Yes/No

The return value. If the record does not exist, the function will terminate
with a run time error, if this optional return value is not used.

General Comments...

If Ok is...

It means that...

True

The record was successfully


deleted

False

The record was not found in the


table.

This function deletes a record from a table. The current key and any filters
bound to the record have no effect on this operation. The record to be
deleted is identified only by the values in its primary key.
In a multi-user environment, another application can delete the record
from the table in the interval between your reading of the record, and your
attempt to delete it. The NAVISION DBMS automatically detects if such
a situation occurs, which causes dbDELREC to fail with a run time error .
To prevent this situation, you can explicitly lock the table by using
dbLOCKTABLE. This causes the table to be locked the entire time during
your operation on the table, preventing other users to acces the table.
Refer to the NAVISION DBMS for further information about table
locking.

For Example...

These AL code samples illustrate how to use the dbDELREC function.


The first example does not use the return value from the dbDELREC
function. This means that a run time error will occur if the record to be
deleted cannot be found. The second example shows how the programmer
explicitly can take care of the error handling by using the return value.
Without using the return value:

Customer.No. := 100:
dbDELREC(Customer);
Using the return value:

Customer.No. := 100;
IF dbDELREC(Customer) THEN
MESSAGE(The customer has been deleted)
ELSE
ERROR(The customer could not be found);

AL Reference Guide

81

dbFIELDACTIVE

dbFIELDACTIVE

Checks whether a field is active or not, that is, whether the field can be
used or not.

Ok := dbFIELDACTIVE(Record.Field)
Record.Field

- Flow: i; Datatypes: RecordField

The field to be checked.


Ok

- Flow: o; Datatypes: Yes/No

This return value reflects whether the field was marked as active or inactive.
If Ok is...

It means that...

TRUE

The field is marked as active

FALSE

The field is marked as in-active

General Comments...

Each field in a record can be set as active or inactive in the table description for the table in question. An inactive field cannot contain data.

For Example...

The following AL code sample shows how to use the dbFIELDACTIVE


function.
The statement:
Customer.Address 2 := Atlanta;

causes a run time error, if the field Address 2 has not been marked as
active. This run time error could be avoided by performing the following
test:
IF dbFIELDACTIVE(Customer.Address 2) THEN
Customer.Address := Atlanta
ELSE
MESSAGE(The field is not marked as active);

82

ALReference Guide

dbFIELDERROR

dbFIELDERROR

Creates an error message for a field, and stops the execution of the code.

dbFIELDERROR(Record.Field [, Text])
Record.Field

- Flow: i; Domain: RecordField

The input field.


Text

- Flow: i; Domain: String

This optional parameter is used to hold text to be printed as part of an


error message.

General comments...

Like with any other run time error, this function will cause any transaction
to be aborted automatically.

For Example...

The following three AL code samples illustrate how to use the dbFIELDERROR function.
Without using the Text parameter:

Customer.No. := ;
dbFIELDERROR(Customer.No.);

This will show the following:


You must specify No.
in the below Customer
Without using the Text parameter:

Customer.No. := NEW 3500;


dbFIELDERROR(Customer.No.);

This will show the following::


No. cannot be NEW 3500
in the below customer
NEW 3500
Using a non-empty string as Text parameter:

Customer.No. := NEW 3500;


dbFIELDERROR(Customer.No., is not specified);

This will show the following::


No. is not specified
in the below Customer
NEW 3500

AL Reference Guide

83

dbFIELDNAME

dbFIELDNAME

Returns the name of a field as a string.

String := dbFIELDNAME(Record.Field)
Record.Field

- Flow: i; Domain: RecordField

A field in a record.
String

- Flow: o; Domain: String

The name of the field given as input.

For Example...

The following AL code samples show how to use the dbFIELDNAME


function.
The statement:
Name := dbFIELDNAME(Customer.No.);

The name of a field identifier is stored in a string. The advantage of the


above statement over:
Name := No.;

is that the first statement dynamically adapts to any changes of field


names made in the development system, while the second statement performs a static assignment.

84

ALReference Guide

dbFIELDNO

dbFIELDNO

Returns the number of a field.

FieldNo := dbFIELDNO(Record.Field)
Record.Field

- Flow: i; Domain: RecordField

The input field


FieldNo

- Flow: o; Domain: Number

The number of the input field.

For Example...

The following AL code sample shows how to use the dbFIELDNO function. The statement:
Number := dbFIELDNO(Customer.No.);

Causes the value 1 to be assigned to the Number variable.


This function is normally used to investigate which field a variable, containing a field number, points out in a record:
CASE CurrentFieldNo OF
dbFIELDNO(Customer.No.): ...
dbFIELDNO(Customer.Name): ...
dbFIELDNO(Customer.Address): ...
ELSE ...
END;

This construct enables appropriate actions to be taken according to which


field number the CurrentFieldNo variable contains.

AL Reference Guide

85

dbFINDREC

dbFINDREC

Finds a record in a table based on the values in key fields in the records.

[Ok :=] dbFINDREC(Record [, Which])


Record

- Flow: io; Domain: Record

On input Rec points out the record to be found. On output there are two
possibilities:
Was the record found? Then...

Which

Yes

The found record is returned in


Record. Any calculated fields used
in this record is set to zero, and
must be updated by dbCALCFIELDS.

No

If the return value Ok is not used, a


run time error will occur.

- Flow: i; Domain: String

Specifies how the search is to be performed. The search continues


through the table until either the record is found or there are no more
records. Each character in the Which string can be present only once. The
=, < and > characters can be combined mutually:
SearchStr

Causes the function to search for ...

'='

A record that equals the key values.(Default)

'>'

A record that is larger than the key values.

'<'

A record that is less than the key values.

'+'

The last record in the table. (+ can only be


used alone)

'-'

The first record in the table. (- can only be


used alone)

Note: If SearchStr contains any of the characters =, > or


<, then values must be assigned to all fields of the current
key and primary key prior to the call of dbFINDREC.

Ok

-Flow: o; Domain: Yes/No

If this optional return value is not used, a run time error occurs if the
record could not be found. When the return value is used, it is assumed
that the programmer takes care of the error handling. The return value can
take the following values:

General Comments...

86

Ok

Means that...

TRUE

The record was found.

FALSE

The record could not be found.

dbFINDREC retrieves the first record that meets the conditions set by
SearchStr and the s filters associated with Rec. The search path corre-

ALReference Guide

dbFINDREC

sponds to the sorting defined by the current key. If the current key is not
the primary key, there is a chance that several values have the same values
in the current key fields. In this case the sorting order defined by the primary key is used as search path.

For Example...

The following AL code sample illustrates how to use the dbFINDREC


function.
OPENWINDOW(Search for a customer number:\ +
#1##################);
WINDOWINPUT(1, Customer."No.");
IF dbFINDREC(Customer) THEN
MESSAGE(The record was found!\ +
Customer No. %1 corresponds to:\ +
%2, Customer."No.", Customer.Name)
ELSE
MESSAGE(Sorry, the record could not be found...);

First the system prompts you to enter a customer number to search for:
Search for a customer number:
____________________________

If you enter a customer number that does not exist, for example
NOBODY, the system will display the following message:
Sorry, the record could not be found...

If you enter a customer number that exists in the Customer table, for
example AAA 1050, the system will display the following message:
The record was found!
Customer number AAA 1050 corresponds to:
AAA Furniture Manufacturing

AL Reference Guide

87

dbGETALLFILTERS

dbGETALLFILTERS

Returns a string containing the filters for all fields in a record.

String := dbGETALLFILTERS(Record)
Record
dbFINDREC

-Flow: i; Domain: Record

The input record.


String

-Flow: o; Domain: String

A string containing filters for all fields in Rec.

For Example...

The following Al code sample illustrates the use of the dbGETALLFILTERS function.
dbSETRANGE("Cust. Ledger Entry".Amount, -100, 100);
dbSETRANGE("Cust. Ledger Entry".Date, 010194D, 123194D);
Str := dbGETALLFILTERS("Cust. Ledger Entry");
MESSAGE(The filters are:\ +
%1, Str);

The message box will show:


The filters are:
Amount:-100..100, Date:010194..123194

88

ALReference Guide

dbGETCURREC

dbGETCURREC

Retrieves a record, marked as current, from the Object Stack.

[OK :=] dbGETCURREC(Record [, Primary])


Record

- Flow: o; data types: Record

The resulting record.


Primary

- Flow: i; Domain: Yes/No

The optional parameter Primary is used to specify whether Record is to


originate from from the primary object, or from the secondary objects..
If...

Enter...

The record must originate from the


TRUE
primary activity
The record may originate from
secondary activities

Ok

FALSE or leave blank (Default)

- Flow: o; Domain: Yes/No

If this optional return value is not used, a run time error occurs if a record
marked as current could not be found. When the return value is used, it is
assumed that the programmer takes care of the error handling. The return
value can take the following values.

General Comments...

If Ok is...

It means that...

TRUE

A record marked as current was


found.

FALSE

A record marked as current could


not be found.

Each object used by your application is assigned a separate area of the


internal Object Stack. The Object Stack is a piece of computer memory set
aside for storing the variables used inside the objects in your application
at run time.
Figure 2 illustrates the Object Stack. In this case the first object on the
Object Stack is a Window object. From this Window object a Report
object is called. Then again a Function object and so on. Records marked
as current are indicated by an x in the left column. (Note that the record
in a window object is automatically marked as current by the system).
The areas for each object can be seen as sub-stacks in which the variables
used in the object are pushed when they are used. The allocation order
order in these sub-stacks are from top to bottom.
dbGETCURREC retrieves a variable (record) marked as current, with
dbSETCURREC, from the Object Stack.
The parameter Primary determines in which activity the search for Record
should take place, or in other words determines the scope of objects to be
searched through. When Primary is TRUE only the active object is

AL Reference Guide

89

dbGETCURREC

searched, whereas FALSE causes the entire Object Stack to be searched


from top to bottom.
Figure 2. The
Object Stack.

Note: When the entire Object Stack is searched, the function


will return the record variable which is found first and is
marked as current.

Object Stack
Automatically
marked as current

Vendor2
Customer1

Vendor1

Search direction

Function

Window
Vendor1
Vendor2
Function
Customer1
Customer2
x Customer3

Allocation
order
Report

Customer1

Window
(Bottom of Object Stack)

For Example...

The following illustates how the dbGETCURREC function searches the


Object Stack.
Assume that the object at the top of the Object Stack executes the following Entry processing code:
.
.
dbGETCURREC(Customer);
dbGETCURREC(Vendor);
.
.

This will cause the system to search the Object Stack from top to bottom.
Figure 3 illustrates an Object Stack holding information about four arbitrary objects, named A, B, C and D.
The search for a Customer record marked as current, will return
Customer1 from object A, while the search for a Vendor record marked as
current, will return Vendor2 from object B.

90

ALReference Guide

dbGETCURREC

Figure 3. An
object Stack holding record values
for four arbitrary
objects.

Object Stack
(x) Customer1
Vendor2
Area for object B
Area for object C
Area for object D

(x) Customer2
(x) Vendor2

Search order

Area for object A

Customer3
(x) Vendor1
(x) Customer1

Another example:

Assume that Window no. 1 (W1) is related to Table no. 1 (T1), and that
Window no. 2 (W2) is related to Table no. 2 (T2). Records in T1 and T2
are called R1 and R2.
Furthermore we assume that W2 is called from W1; thus the active window will be W2. Text is being typed into W2 which causes the connected
Entry processing code to be activated.
Entry processing code for T2:
dbGETCURREC(R2,TRUE)
dbGETCURREC(R2,FALSE)
dbGETCURREC(R1,FALSE)
dbGETCURREC(R1,TRUE)

OK:=dbGETCURREC(R1,TRUE)

Returns the record R2 on which


the cursor is placed in W2.
Returns the record R2 on which
the cursor is placed in W2.
Returns the record R1 on which
the cursor is placed in W1.
Results in a run-time error as
R1 does not belong to the T2
table with the current window
W2.
Returns the value OK=FALSE.

AL Reference Guide

91

dbGETFILTER

dbGETFILTER

Returns a text string containing the filter for a field.

String := dbGETFILTER(Record.Field)
Record.Field

-Flow: i; Datatypes: RecordField

The input field.


String

-Flow: o; Datatypes: String

An output string containing the filter for the input field.

General Comments...
For Example...

Se also dbSETFILTER and dbSETRANGE.


The following AL code sample shows how to use the dbGETFILTER
function.
dbSETRANGE(CustomerEntry.Amount, -100, 100);
Str := dbGETFILTER(CustomerEntry.Amount);
MESSAGE(The filter is:\ +
%1, Str);

The message box will show:


The filter is:
-100..100

92

ALReference Guide

dbGETREC

dbGETREC

Finds a record based on values in a primary key field.

[Ok :=] dbGETREC(Record [, Value, ...])


Record

- Flow: o; Domain: Record

The record found in the table.


Value,...

- Flow: i; Domain: -

Denotes the value(s) in the primary key fields . The type of Value(s) must
match the type of the corresponding primary key field(s). Unspecified
fields are assigned the value 0 or an empty string.
Ok

- Flow: i; Domain: Yes/No

If this optional return value is not used, a run time error occurs if the
record could not be found. When the return value is used, it is assumed
that the programmer takes care of the errror handling. The return value
can take the following values

General Comments...

For Example...

If Ok is...

It means that...

TRUE

The record was found.

FALSE

The record could not be found.

The function always uses the primary key for the table and ignores filters,
if any. After the function call, the current key and filters are not changed.
The following AL code sample shows how to use the dbGETREC function. The following statement:
dbGETREC(Customer, '1120');

will cause a run time error if the customer with the number 1120 cannot
be found. In order to avoid this, use the following construct:
IF dbGETREC(Customer, 1120) THEN
MESSAGE(The record was found)
ELSE
MESSAGE(Sorry, the record could not be found...);

AL Reference Guide

93

dbGLOBALFILTER

dbGLOBALFILTER

Makes filters assigned to a table global.

dbGLOBALFILTER(Record)
Record

- Flow: i; Data types: Record

A record from the table to which filters already are assigned. Any filters
assigned to the fields in the record are made global.

General Comments...

Normally the dbGLOBALFILTER function is used only to control special


accounting matters.
A global filter is active from the point in time when dbGLOBALFILTER
is called, and until the system exits the AL code module containing the
function.
In an AL code module a global filter can be temporarily removed by setting other filters which overwrites the global filter or by removing all filters using dbRESETTABLE(Record, TRUE).
When the system exits the AL code module, the global filters will, however, be reset to the original. Please note that a global filter only can be
changed from within the AL code. Thus, it is not possible for the end user
to change the filter in for example a window. Futhermore it is not possible
for the end user to see the current global filter.
A global filter is associated to a specific company. If you use dbSELECTCOMPANY(Record, Company) to select a new company, any filters set
on Record in the initial company will be transferred to Record. But if one
or more of the filters have status as global filters, this status will be
removed.
When you return to the initial company, the status is automatically reestablished for the global filters on Record. If, in the meantime, the filters
for Record were removed, the global filter must be reestablished by
dbRESETTABLE(Record, FALSE).

For Example...

The following shows how to use the dbGLOBALFILTER function


We assume that you want to prevent users from seeing G/L entries outside
the range: January 1, 1994 to December 31, 1994. Then you can add the
following to Function 1 in your application:
dbSETRANGE(G/L Entry.Date,'010194','123194');
dbGLOBALFILTER(G/L Entry);
callMENU(MainMenu);

Now the user will not be able to see G/L entries outside the specified
range in the chart of accounts, reports, batch jobs and so on.

94

ALReference Guide

dbINITREC

dbINITREC

Initializes a record.

dbINITREC(Record)
Record

- Flow: io; Data types: Record

The record to be initialized. The dbINITREC function assigns default values to each field in the record. The values correspond to those defined
when the table was created . Fields for which no values were defined, are
assigned the following default values:
Data type...

Default value...

Yes/No

No

Option

Integer

Long Integer

Decimal No.

0.0

Date

0D (Undefined date)

Time

0T (Undefined time)

Code

'' (empty string)

Text

'' (empty string)

Note: The primary key fields are not initialized.

General Comments...

For Example...

After this operation, you are free to change the values in any or all of the
fields before calling dbINSREC to enter the record in the table. Be sure
that the field(s) comprising the primary key contain values that make the
total primary key unique. If the primary key is not unique (record already
exists), the DBMS will reject the record.
These AL code samples shows how to use the dbINITREC function.
Assume that the primary key includes the No. field. Consider the following situations:
Situation 1:

dbINITREC(Customer);
Customer.No. := 1120;
dbINSREC(Customer);
Situation 2:

Customer.No. := 1120;
dbINITREC(Customer);
dbINSREC(Customer);

As dbINITREC does not initialize the primary key fields, the sequence of
the statements in situation 1 and 2 is not important. Situation 1 causes the
same result as situation 2.

AL Reference Guide

95

dbINSREC

dbINSREC

Inserts a record in a table.

[OK :=] dbINSREC(Record)


Record

- Flow: i; Datatypes: Record

The record to be inserted. Rec itself does not change.


Ok

- Flow: o; Datatypes: Yes/No

If this optional return value is not used, a run time error occurs if the
record could not be inserted. When the return value is used, it is assumed
that the programmer takes care of the errror handling. The return value
can take the following values
If Ok is...

It means that...

TRUE

The record was inserted.

FALSE

The record could not be inserted.

General Comments...

A record is uniquely identified by the values of the primary key fields.


The NAVISION DBMS inspects the primary key for the table before
inserting a new record.

For Example...

The following AL code samples show how to use the dbINSREC function.
Without using the return value:

dbINITREC(Customer);
Customer.No. := 1120;
dbINSREC(Customer);

If for example the customer number 1120 already exists, a run time error
will occur. In order to avoid this, use the following construct:
Using the return value:

dbINITREC(Customer);
Customer.No. := 1120;
IF dbINSREC(Customer) THEN
MESSAGE(Customer no.: %1 inserted, Customer.No.)
ELSE
MESSAGE(Sorry, an error occured...);

96

ALReference Guide

dbLOCKTABLE

dbLOCKTABLE

Locks a table to protect it from conflicting write transactions.

dbLOCKTABLE(Record [, Wait] [, VersionCheck])


Record

-Flow: i; Datatypes: Record

A record from the table to be locked.


Wait

- Flow: i; Datatypes: Yes/No

The Wait parameter specifies what action to take in case the table already
is locked:
To...

Enter...

make the system wait until the


table is unlocked, if another appli- TRUE or leave blank (Default)
cation already has locked the table
make dbLOCKTABLE terminate
with a run time error, if another
FALSE
application already has locked the
table

VersionCheck

- Flow: i; Datatypes: Yes/No

This optional parameter is used to activate a version check.


To...

Enter...

de-activate version check

FALSE (or leave blank)

activate version check

TRUE

The example below illustrates how to use the version check.

General comments

Because all write operations automatically lock the table in use, the
dbLOCKTABLE would seem unnecessary. Imagine however a transaction in which an application wants to inspect data and then only possibly
change it, with a guarantee that the data being changed has not been modified by other applications since the read. The solution is to explicitly lock
the table before the read operation, thereby ensuring that no other application makes changes between the read and the possible write.
Refer to The NAVISION DBMS for an explantion of table locking.

For Example...

The following examples illustrate how to use the dbLOCKTABLE function.


Without using the optional parameter VersionCheck:

Figure 4 illustrates the scope of write locks in an AL code module. The


figure illustrates both an explicit lock and an automatic lock.
Line (1) in the write transaction explicitly locks table A. If this explicit
lock was not set on table A, the DBMS would automatically lock this
table when a record is inserted (3). Table B is not locked explicitly, but is
locked automatically by the DBMS when a record is inserted (4). Both

AL Reference Guide

97

dbLOCKTABLE

locks are active until the system exits the AL code module (5).
.

Table Locking
.
.

{Enter AL code module}


dbLOCKTABLE(TableA);
dbFINDREC(TableA, ...);
.
.
.

Table B locked

Table A locked

Figure 4. The
scope of write
locks

(1)
(2)

dbINSERTREC(TableA, ...);

(3)

dbINSERTREC(TableB, ...);

(4)

.
.
.
.
.

{Exit AL code module}

(5)

.
.

Using the optional parameter VersionCheck:

If a data update depends on a preceding reading, and there is relatively


long time between the reading and the writing, it might not be suitable to
lock a table as normally done during a transaction, as this means that
other users will not be able to update the table until your transaction is
committed. The solution is to use the VersionCheck parameter, which
causes the system to check if a record has been changes, by comparing
time-stamps.
Consider the following example:
dbGETREC(Customer, AAA 1050);
.
.
dbLOCKTABLE(Customer, TRUE, TRUE);
.
dbMODIFYREC(Customer);
.
.
{End write transaction}

{Reading at 10:00. Time stamp}


{automatically set to 10:00}
{Starting write transaction}
{at 10:15}
{A run time error will occur}
{if the time-stamp on}
{Customer in the data version}
{used in the transaction, is
different from the time-stamp
made the last time you read
the record before the write
transaction started}

When the VersionCheck parameter is TRUE, dbMODIFYREC will compare the time-stamp on the record you modify within a transaction, with
the time-stamp made the last time you read the record before the transaction started. If these time-stamps are different, the record has been
changed in the period from you read and until you locked the table, and a
run time error will occur.

98

ALReference Guide

dbMARKEDONLY

dbMARKEDONLY

Causes a special filter to be activated: your view of the table will only
include records marked by dbRECMARK.

OK := dbMARKEDONLY(Record [, MarkedOnly])
Record

- Flow: i; Datatypes: Record

A record from the table for which you want to activate the special
markfilter.
MarkedOnly

- Flow: i; Datatypes: Yes/No

This optional parameter changes the state of the special filter.

Ok

To...

Enter...

Include only marked records

TRUE

Include all records.

FALSE

- Flow: o; Datatypes: Yes/No

The return value reflects whether the special filter is activated.


If Ok is...

For Example...

It means that...

TRUE

The special filter is in use.

FALSE

The special filter in not in use.

The following AL code sample illustrates how to use the dbRECMARK


together with dbMARKEDONLY. Assume that initially none of the
records are marked.
dbSELECTKEY(Customer."No.");
Customer."No." := NEW 3500;
dbFINDREC(Customer, =);
dbRECMARK(Customer, TRUE);

{Mark a record}

No1 := dbRECCOUNT(Customer);
dbMARKEDONLY(Customer, TRUE);
No2 := dbRECCOUNT(Customer);
MESSAGE(Number of records before dbMARKEDONLY: %1\ +
Number of records after dbMARKEDONLY: %2, No1, No2);

The message box will show:


Number of records before dbMARKEDONLY: 5
Number of records after dbMARKEDONLY: 1

AL Reference Guide

99

dbMAXRANGE

dbMAXRANGE

Returns the maximum value for a range for a field in a record.

Value := dbMAXRANGE(Record.Field)
Record.Field

- Flow: i; Datatypes: RecordField

The input field. The current filter on Record.Field must be a single range,
otherwise a run time error occurs.
Value

- Flow: o; Datatypes: -

A return value containing the maximum value of the range set for
Record.Field. The type of value must match the type of Record.Field.

For Example...

The following AL code samples illustrate how to use the dbMAXRANGE


function.
A filter which is a single range:

dbSETFILTER(Customer."No.",'100..200');
Val := dbMAXRANGE(Customer."No.");
MESSAGE(The maximum value is: %1, Val);

The message box will show:


The maximum value is: 200
A filter which is a single value:

dbSETFILTER(Customer."No.",'100');
Val := dbMAXRANGE(Customer."No.");
MESSAGE(The maximum value is: %1, Val);

The message box will show:


The maximum value is: 100
A filter which is not a single range:

dbSETFILTER(Customer."No.",'200|300');
Val := dbMAXRANGE(Customer."No.");

This causes a run time error to occur.

100

ALReference Guide

dbMINRANGE

dbMINRANGE

Returns the minimum value for a range for a field in a record.

Value := dbMINRANGE(Record.Field)
Record.Field

- Flow: i; Datatypes: RecordField

The input field. The current filter on Record.Field must be a single range,
otherwise a run time error occurs.
Value

- Flow: o; Datatypes: String

A return value containing the minimum value of the range set for
Record.Field. The type of Value must match the type of Record.Field.

For Example...

The following AL code samples illustrates how to use the dbMINRANGE


function.
A filter which is a single range:

dbSETFILTER(Customer."No.",'100..200');
Val := dbMINRANGE(Customer."No.");
MESSAGE(The minimum value is: %1, Val);

The message box will show:


The minimum value is: 100
A filter which is single value:

dbSETFILTER(Customer."No.",'100');
Val :=dbMINRANGE(Customer."No.");
MESSAGE(The minimum value is: %1, Val);

The message box will show:


The minimum value is: 100
A filter which is not a single range:

dbSETFILTER(Customer."No.",'200|300');
Val := dbMINRANGE(Customer."No.");

This causes a run time error to occur.

AL Reference Guide

101

dbMODIFYALL

dbMODIFYALL

Modifies a field in all records within a specified range in a table.

dbMODIFYALL(Record.Field, Value)
Record.Field

- Flow: i; Datatypes: RecordField

The field to be modified.


Value

Flow: i; Datatypes: -

The value to be assigned to the field Record.Field in all records. The type
of Value must match the type of Record.Field.

General Comments...

If no filter is set, the field is modified in all records in the table. Otherwise dbMODIFYALL will only change the fields in the records within the
range(s) specified by the filter.

For Example...

The following AL code samples illustrate how to use the dbMODIFYALL


function.
The function of the statement:
dbMODIFYALL(Customer.Group, 2);

equals the following:


IF dbFINDREC(Customer, -) THEN
REPEAT
Customer.Group := 2;
dbMODIFYREC(Customer);
UNTIL dbNEXTREC(Customer) = 0;

But the use of dbMODIFYALL is much faster, as only one server access
is needed. The second method requires several server accesses.

102

ALReference Guide

dbMODIFYREC

dbMODIFYREC

Modifies a record in a table.

[Ok :=] dbMODIFYREC(Record)


Record

- Flow: i; Datatypes: Record

The record to be modified. Record itself does not change.


Ok

- Flow: o; Datatypes: Yes/No

If this optional return value is not used, a run-time error occurs if the
record does not exist. Using the value, it is possible for the programmer to
take care of the error handling. The possible values are: .

General Comments...

If Ok is...

It means that...

TRUE

The record was successfully modified

FALSE

The record could not be found in


the table

The record to be replaced is identified by the primary key fields. The current key and filters on the record does not affect this operation.
In multi user environments, another application can modify the record in
the table in the interval between your reading the record and your attempt
to modify it. The NAVISION DBMS automatically detects this, which
causes the dbMODIFYREC function to terminate with a run time error.

For Example...

The following AL code sample illustrates how to use the dbMODIFYREC function.
Customer."No." := AAA 1050;
dbFINDREC(Customer, =);
{Find customer}
MESSAGE(The customer no. %1 corresponds to:\ +
%2, Customer."No.", Customer.Name);{Display name}
WAITKEYPRESS;
Customer.Name := Joe Blow;
dbMODIFYREC(Customer);
{Modify customer name}
MESSAGE(Now customer no. %1 corresponds to:\ +
%2, Customer."No.", Customer.Name);{Display modified
name}

When a specific customer has been located, the message box shows:
The customer no. AAA 1050 corresponds to:
AAA Furniture Manufacturing

After the information has been modified, the second message box shows:
Now customer no. AAA 1050 corresponds to:
Joe Blow

AL Reference Guide

103

dbNEXTREC

dbNEXTREC

Steps through a specified number of records to retrieve a record.

ActualSteps := dbNEXTREC(Record [, Steps])


Record

- Flow: io; Datatypes: Record

The record from which the search shall begin. Return the retrieved record.
Steps

- Flow: i; Datatypes: Number

Is used to define the direction of the search, according to the following


rules:
Value of Steps

Effect...

>0

Search Steps records forwards in


the table.

<0

Search Steps records backwards


in the table.

=0

No effect

If Steps is not specified, the next record is found.


ActualSteps

- Flow: o; Datatypes: Number

The function returns the number of records traversed which meet the criteria of any filters and the current key. This value can be closer to zero
than Steps, depending upon the number of records in the table. If the
table is empty, zero is returned and Record remains unchanged

General Comments..

For Example...

dbNEXTREC locates a record positioned a given number of steps forward or backward from Record. Movement through the table is governed
by the filters and the current key associated with the records. The fields in
Record which will be compared with the current key fields, must contain
appropriate values before the function is called.
The following AL code sample illustrates how to use the dbNEXTREC
function.
Count := 0;
IF dbFINDREC(Customer, -) THEN
REPEAT
Count := Count + 1;
UNTIL dbNEXTREC(Customer) = 0;

The above code uses a REPEAT UNTIL loop to count the number of
entries in the Customer table. The dbFINDREC function is used to find
the first entry in the table. Each time dbNEXTREC is called, it steps one
record forward. When dbNEXTREC = 0 there are no more entries in the
table and the system exits the loop.

104

ALReference Guide

dbRECCOUNT

dbRECCOUNT

Counts the number of records in a table.

Number := dbRECCOUNT(Record)
Record

- Flow: i; Datatypes: Record

A record from the table to be counted.


Number

- Flow: o; Datatypes: Number

The number of records.

General Comments...

The dbRECCOUNT function returns the number of records that meet the
conditions of any filters associated to the records. If no filters are set, the
function returns the total number of records in the table.
The number of filters applied to the records affects the speed of the
dbRECCOUNT function. The operation is fastest when no filters are
applied.

For Example...

The following illustrates how to use the dbCOUNTREC function. The


first statement:
Number :=dbRECCOUNT(Customer);

assigns the number of records in the Customer table to the Number variable. This statement equals:
Count := 0;
IF dbFINDREC(Customer, -) THEN
REPEAT
Count := Count + 1;
UNTIL dbNEXTREC(Customer) = 0;

However, the first example is much faster, as only one command to the
DBMS is needed. The second example requires that several command
are send to the DBMS.

AL Reference Guide

105

dbRECMARK

dbRECMARK

Marks a record.

Ok := dbRECMARK(Record [, Mark])
Record

- Flow: i; Datatypes: Record

The input record.


Mark

Ok

- Flow: i; Datatypes: Yes/No


To...

Enter...

Mark the record. (Already marked


records remains marked).

TRUE

Remove existing mark, if any.

FALSE

- Flow: o; Datatypes: Yes/No

The return value reflects whether Record is marked.


If Res is...

It means that...

TRUE

The record is marked.

FALSE

The is not marked.

The mark is valid until the execution of the application ends.

For Example...

The following AL code sample illustrates how to use the dbRECMARK


function. Assume that initially no records are maked.
dbSELECTKEY(Customer."No.");
Customer."No." := NEW 3500;
dbFINDREC(Customer, =);
dbRECMARK(Customer, TRUE);

{Mark a record}

dbFINDREC(Customer, -);
{Find first record}
REPEAT
{Iterate through records}
Marked := dbRECMARK(Customer);
{Test if marked?}
MESSAGE(Customer.No.:%1, Marked?: %2, Customer."No.", Marked);
UNTIL dbNEXTREC(Customer) = 0;

The example illustrates that dbRECMARK both can be used to explicitly


mark a record, and to test whether a record is marked or not.
The message boxes will show:
Customer No.: AAA 1050, Marked?: No
Customer No.: DEL 3500, Marked?: No
Customer No.: NEW 3500, Marked?: Yes
Customer No.: PEA 2500, Marked?: No

106

ALReference Guide

dbRECMARK

Customer No.: SOP 7500, Marked?: No

The next example shows how to used the dbRECMARK together with
dbMARKEDONLY. Assume that initially none of the records are marked.
dbSELECTKEY(Customer."No.");
Customer."No." := NEW 3500;
dbFINDREC(Customer, =);
dbRECMARK(Customer, TRUE);

{Mark a record}

No1 := dbRECCOUNT(Customer);
dbMARKEDONLY(Customer, TRUE);
No2 := dbRECCOUNT(Customer);
MESSAGE(Number of records before dbMARKEDONLY: %1\ +
Number of records after dbMARKEDONLY: %2, No1, No2);

The message box will show:


Number of records before dbMARKEDONLY: 5
Number of records after dbMARKEDONLY: 1

AL Reference Guide

107

dbRESETTABLE

dbRESETTABLE

Removes all filters (also special filters set by dbMARKEDONLY), and


changes the current key to the primary key.

dbRESETTABLE(Record [, GlobalFilters])
Record

- Flow: io; Datatypes: Record

A record from the table to be resat.


GlobalFilters

- Flow: i; Datatypes: Yes/No

This optional parameter determines whether global filters are to be


removed.

General Comments...

For Example...

To...

Enter...

Prevent global filters from being


removed.

FALSE (Default value).

Remove global filters.

TRUE

Refer to the function dbGLOBALFILTER for additional information


about global filters.
The following AL code sample illustrates how to use the dbRESETTABLE function.
dbSETFILTER(Customer."No.", NEW 3500);
dbSETRANGE(Customer."Salesperson Code", B. SANDERS);
dbMARKEDONLY(Customer, TRUE);
Count := dbRECCOUNT(Customer);
{Count the marked customers
within a specified range}
dbRESETTABLE(Customer);
Count1 := dbRECCOUNT(Customer);
{Count all customers}
MESSAGE(Before dbRESETTABLE: %1\ +
After dbRESETTABLE: %2, Count, Count1);

The message box will show:


Before dbRESETTABLE: 0
After dbRESETTABLE: 5

The example illustrates that all filters have been removed by the call of
dbRESETTABLE.

108

ALReference Guide

dbSELECTCOMPANY

dbSELECTCOMPANY

Redirects references to table data to another company.

[Ok :=] dbSELECTCOMPANY(Record [, CompanyName])


Record

- Flow: i; Datatypes: Record

A record from a table to be accessed in another company.


CompanyName

-Flow: i; Datatypes: String

The name of the company to be accessed. If CompanyName is not specified, the initial company is used.
Ok

- Flow: o; Datatypes: Yes/No

If this optional return value is not used, a run-time error occurs if the company does not exist. Using the value, it is possible for the programmer to
take care of the error handling. The possible values are:

General Comments...

If Ok is...

It means that...

TRUE

The company was found.

FALSE

The company could not be found.

This function respects the users authorization. Thus, a user cannot access
data in CompanyName, unless the user already have the necessary authorization assigned by a super user.
The dbSELECTCOMPANY function is not affected by dbRESETTABLE. The only way to deselect a company, is by a new call to dbSELECTCOMPANY.
Global filters always belong to a specific company. If you use dbSELECTCOMPANY(Record, 'NewCompany') to select the company named
'NewCompany', any filters assigned to Record will be transferred to
Record in the new company; but if any of the filters have status as a global filter, this status will be removed.
When you return to the initial company, the global filters are automatically reestablished for Record. If the filters for Record have been
removed, the global filters can be restablished by calling dbRESETTABLE (Record,FALSE).

For Example...

The following AL code sample illustrates how to use the dbSELECTCOMPANY function.
dbSELECTCOMPANY(G/L Account,'New Company');
dbGETREC(G/L Account,'1000');
dbCALCFIELDS(G/L Account.Balance); {Calculates the balance for
account no. 1000 in the Company named 'New Company'}
dbSELECTCOMPANY(G/L Entry,'New Company');
dbSELECTKEY(G/L Entry.No.,FinanceEntry.Date);
dbSETRANGE(G/L Entry.No.,'1000');

AL Reference Guide

109

dbSELECTCOMPANY

dbSETRANGE(G/L Entry.Date,010194D,013194D);
dbSUM(G/L Entry.NettAmount);
{Sums NettAmount from all G/L
entries on account no. 1000
within the specified range,
for the company 'New Company'}
dbRESETTABLE(G/L Entry);
dbFINDREC(G/L Entry,'+');
{Finds the largest No. in
table G/L Entry in the Company
'New Company'}
dbDELREC(G/L Entry);
{Deletes this entry in the
company 'New Company'}

The above example illustrates that when dbSELECTCOMPANY has


been called, all future references to the G/L Account and the G/L
Entry tables will refer to the table data in the company New Company.

110

ALReference Guide

dbSELECTKEY

dbSELECTKEY

Selects a key for a table.

[Ok :=] dbSELECTKEY(Record.Field [, Record.Field, ...])


Record.Field

- Flow: i; Datatypes: RecordField

One or more fields identifying the key to be selected.


Ok

- Flow: o; Datatypes: Yes/No

If this optional return value is not used, a run-time error occurs if the key
does not exist. Using the value, it is possible for the programmer to take
care of the error handling. The possible values are:

General Comments...

If Ok is...

It means that...

TRUE

The key was successfully


selected.

FALSE

The key does not exist.

dbSELECTKEY assigns a specified key to a record. The key becomes the


current key and is used by dbFINDREC, dbNEXTREC and other functions until another another key is selected. Until this function is called, the
primary key for the table is used as current key.
Non-active fields are automatically ignored. When searching in the key
table, the first occurrence of the specified fields is always selected. This
implies that the selected may be more comprehensive than specified.
Only active keys are scanned.
Note: To choose from the list of active keys while in the AL
editor, press F5 to enter the Symbols survey. Highlight a table
variable and press F6 twice.

Refer to The NAVISION DBMS for an explanation about keys.

For Example...

The following AL code samples show how to use the dbSELECTKEY


function.
Without using the return value:

dbSELECTKEY(Customer.Name);

The above statement selects the Name key for the Customer table. If the
system cannot find the desired key a run time error occurs.
Using the return value:

By using the return value, a run time error can be avoided, if the system
cannot find the desired key.
IF dbSELECTKEY(Customer.Name) THEN
MESSAGE(The key was successfully selected!)
ELSE
MESSAGE(Sorry, the key could not be found...);

AL Reference Guide

111

dbSETCURREC

dbSETCURREC

Marks a record as current record.

dbSETCURREC(Record)
Record

- Flow: i; Datatypes: Record

The record to be marked as current.

General Comments...

In each object containing AL code, only one record per table can be
marked as current record. Attempts to mark two different records from
the same table as current in the same object will cause a run time error.
In Window objects, the record selected by the cursor will automatically be
marked as current record, when calling sub-objects.
A mark will be valid until the system exits the AL code module in which
dbSETCURREC was used.
Please observe that it is not the contents of the record, but the address of
the record which is stored as current record. If the contents of the record is
changed after dbSETCURREC has been called, then it is possible to get
hold of the contents of the record, using dbGETCURREC.

For Example...

The following explains how to use the dbSETCURREC function.


If this function is used in Entry processing code, and the code calls a
report (callREPORT), then it is possible to gain access to the current
record using dbGETCURREC.
This is for example used in the NAVISION demo application when concurrently posting and printing an invoice. The following three steps must
be accomplished:
1. Call a function which can perform the posting.
2. Call dbSETCURREC(InvoiceHeader).
3. Use callREPORT to call a report to take care of the printing.

In the report, the report element InvoiceHeader must have the option field
Current Record set to Yes.
The result is that only one invoice will be printed, corresponding to
InvoiceHeader, as set by dbSETCURREC(InvoiceHeader).

112

ALReference Guide

dbSETFILTER

dbSETFILTER

Assigns a filter to a specified field.

dbSETFILTER(Record.Field, String [, Value, ...])


Record.Field

- Flow: i; Datatypes: RecordField

The field to be filtered.


String

- Flow: i; Datatypes: String

The filter expression. A valid expression consists of alphanumeric characters and one or more of the following operators: <, >, ?, &, | and
=. Furthermore replacement fields (%1, %2, ...) can be used to insert
values at run time.
Value

- Flow: i; Datatypes: -

Replacement values to be inserted at replacement fields in the filter


expression. The type of Value must match the type of Record.Field.

General Comments...

If the function is called with a field for which a filter already exists, this
filter will be removed before the new one is set.
Filters can be constructed, using the following operators
Operator...

Explanation...

..

Range

&

And

Or

<

Less than

<=

Less than or equal to

>

Greater than

>=

Greater than or equal to

<>

Different from

Forms a part of value

The following operators can only be used for date fields :


Operator

Explanation...

Current period

Px..Py

Period x to y

Current year

Read more about filters in The NAVISION DBMS.

For Example...

The following table show examples of filters.


Example...

Explanation...

A..Z

range from A to Z

A|G

A or G

AL Reference Guide

113

dbSETFILTER

Example...

Explanation...

F.. & ?A/S

from F and A/S is included in the


field.

<>B

all except B

<>''

all not blank

<=200 | >500

all less than or equal to 200 or


greater than 500

The following AL code shows how to use the dbSETFILTER function.


Using a filter with replacement fields:

dbSETFILTER("G/L Account"."No.", %1..%2|%3, 100, 200, 300);

Selects all accounts in the range from 100 to 200 and account no. 300
Using a filter entered directly in a string:

The following filter which is entered directly in a string, correspond to


the above example.
dbSETFILTER("G/L Account"."No.", 100..200|300);

Selects all accounts in the range from 100 to 200 and account no. 300.

114

ALReference Guide

dbSETRANGE

dbSETRANGE

Sets a simple filter (a single range or a single value) on a field.

dbSETRANGE(Record.Field [, FromValue] [, ToValue])


Record.Field

- Flow: i; Datatypes: RecordField

The field to be filtered.


FromValue

- Flow: i; Datatypes: -

The lower limit of the range. The type of FromValue must match
Record.Field.
ToValue

- Flow: i; Datatypes: .

The upper limit of the range. If ToValue is not specified, dbSETRANGE


will use the same value as specified for FromValue. The type of ToValue
must match Record.Field.

General Comments...

dbSETRANGE provides a quick way to set a simple filter on a field.


If the function is called with a field for which a filter already exists, this
filter will be removed before the new one is set.
If none of the optional parameters are specified, dbSETRANGE will
remove the filter set for Record.Field (if any).
Read more about filters in The NAVISION DBMS.

For Example...

The following AL code sample illustrates how to use the dbSETRANGE


function to specify that you only what to see customers with numbers in
the range 100 to 200.
dbSETRANGE(Customer.No., 100, 200);

The above statement is a quick way to set the same filter as:
dbSETFILTER(Customer.No., >=100&<=200);

AL Reference Guide

115

dbSUM

dbSUM

Sums specified columns in a table.

dbSUM(Record.Field [, Record.Field, ...])


Record.Field

- Flow: io; Datatypes: RecordField

One or more fields which are defined as sum fields in the current key. All
the fields specified must be defined as sum fields in the current key, otherwise a run time error will occur.

General Comments...

dbSUM adds up columns in a table. The function operates only on records


which meet the conditions of any filters associated with the record.
Refer to The NAVISION DBMS for an explanation about the concept
of sum fields.

For Example...

The following AL code illustrates how to use the dbSUM function.


dbSELECTKEY("Cust. Ledger Entry"."Customer No.","Cust. Ledger Entry".Date);

dbSETRANGE("Cust. Ledger Entry"."Customer No.", AAA 1050);


dbSETRANGE("Cust. Ledger Entry".Date, 010194D, 123194D);
dbSUM("Cust. Ledger Entry".Amount);

The first line selects a key. The second and third lines sets filters for the
fields Customer No and Date in the record Cust. Ledger Entry so that
the sum is only calculated within the specified range. dbSUM is then used
to find the movement in the account 'AAA 1050' for 1994, and the result
is placed in the Amount field.

116

ALReference Guide

dbTESTFIELD

dbTESTFIELD

Tests if the contents of a field matches a value. If not, an error message is


displayed.

dbTESTFIELD(Record.Field [, Value])
Record.Field

- Flow: i; Datatypes: RecordField

The field to be tested.


Value

- Flow: i; Datatypes: -

The value to be matched with Record.Field. The type of Value must


match the type of Record.Field. If this optional parameter is not specified, the test value is set to 0 or the empty string.

For Example...

The following AL code samples illustrates how to use the dbTESTFIELD


function.
Without using the optional Value:

Customer.No. := ;
dbTESTFIELD(Customer.No.)

Returns an error message:


You must specify No.
in the below Customer
Using the optional value:

Customer.No. := AAA 1050;


dbTESTFIELD(Customer.No.,'1000')

If No. is different from 1000, the following error message will be shown:
No. must be 1000
in the below Customer
AAA 1050
An analogy:

The dbTESTFIELD function corresponds to:


IF Record.Field <> Value THEN
dbFIELDERROR(Record.Field);

AL Reference Guide

117

dbTRANSFERFIELDS

dbTRANSFERFIELDS

Copies all matching fields in one record to another record, according to


the specification in the Field Transfer Table (Design, Tables, Field Transfers).

dbTRANSFERFIELDS(FromRecord, ToRecord [, All])


FromRecord

- Flow: i; Datatypes: Record

The record to be copied from.


ToRecord

- Flow: o; Datatypes: Record

The matching field values are assigned to fields in this record.


All

- Flow: i; Datatypes: Yes/No

This optional parameter determines whether all fields should be copied,


or if the Field Transfer Table should be used.
To...

Enter...

copy all fields, independent of the


transfer tablea.

TRUE

copy based on the Field Transfer


Table.

FALSE or leave blank (Default)

a. Only fields which exists in both records will be copied, and


only if they have the same field type.

General Comments...

FromRecord and ToRecord may come from two different tables.

General
For
Comments...
Example...

The following Al code sample shows how to use the dbTRANSFERFIELDS function.
dbTRANSFERFIELDS can be used to automatically fill in an Invoice
Header, based on a Customer no.
{Assume that the user has entered a Customer no.}
{in Invoice Header.CustomerNo.}
dbGETREC(Customer, Invoice Header.CustomerNo.);
{Finds a customer in the Customer table}
dbTRANSFERFIELDS(Customer, Invoice Header);

Based on a customer no., a customer is found in the Customer table. The


fields in the Invoice Header record, is automatically filled in according
to the Field Transfer Table.

118

ALReference Guide

DELCHR

DELCHR

Deletes one or more characters in a String.

NewString := DELCHR(String [, Where] [, Which])


String

- Flow: io; data types: String

The input string.


Where

- Flow: i; data types: String

A string used to specify where deletion is to be made. The Where string


may contain one or more of the following characters:

Which

Where

Effect

Any character in String which


matches a character in Which will
be deleted from String. This is the
default value.

<

Any leading character in String


which matches a character in
Which will be deleted from String.

>

Any trailing character in String


which matches a character in
Which will be deleted from String.

- Flow: i; data types: String

A string containing the characters to be deleted. The DELCHR function is


case-sensitive.
If Which is not specified, = will be used as default.
NewString

- Flow: o; data types: String

The resulting string.

For Example...

This example shows how to use the DELCHR function.


Str := 'Windy Solutions';
Where := '<>';
Which := 'Ws';
NewStr := DELCHR(Str, Where, Which);
MESSAGE('>%1<, is transformed to: >%2<', Str,NewStr);

The two strings are shown in the message-box as:


>Windy Solutions<, is transformed to: >indy Solution<

AL Reference Guide

119

DELSTR

DELSTR

Deletes a sub-string in a String.

NewString := DELSTR(String, Position [, Length])


String

- Flow: i; data types: String

The input string.


Position

- Flow: i; data types: Number

The position of the first characcter to be deleted. Position must be greater


than 0 (zero).
Note: If Position exceeds the length of String, DELSTR will
return the entire original string.

Length

- Flow: i; data types: Number

Specifies the number of characters to be deleted. Must be greater than 0.


If Length is not specified, all characters from Position and to the end of
the string will be deleted.
NewString

- Flow: o; data types: String

The resulting string.

For Example...

The following illustrates how to use the DELSTR function.


Str := 'Adjusting Prices - Please wait';
Position := 11; {Remove the word 'Prices' and a blank}
Length := 7;
NewStr := DELSTR(Str, Position, Length);
MESSAGE('The original string:\ >%1<', Str);
MESSAGE('The modified string:\ >%1<', NewStr);

The first message-box above will show the following:


The original string:
>Adjusting Prices - Please wait<

...while the second message-box will show:


The modified string:
>Adjusting - Please wait<

120

ALReference Guide

DMY2DATE

DMY2DATE

Returns a date based on Day, Month and Year.

Date := DMY2DATE(Day [, Month] [, Year])


Day

- Flow: i; data types: Number

The number of the day in the month (1..31).


Month

- Flow: i; data types: Number

The number of the month (1..12). If this optional parameter is not used,
the system will use the current month as default.
Year

- Flow: i; data types: Number

Ranges from 1980 to 2059. If this optional parameter is not used, the system will use the current year as default.
Date

- Flow: o; data types: Date

The resulting date.

For Example...

This AL code sample shows how to use the DMY2DATE function.


Day := 11;
Month := 2;
Year := 1996;
D := DMY2DATE(Day, Month, Year);
MESSAGE('Day no. %1,\ +
'month no.%2, and\' +
year no. %3 \' +
'corresponds to the date: %4', Day, Month, Year, D);

The message box will show:


Day no. 11
month no. 2, and
the year 1996
corresponds to the date: 02/11/96

AL Reference Guide

121

DWY2DATE

DWY2DATE

Returns a Date based on a WeekDay, a Week and a Year.

Date := DWY2DATE(WeekDay [, Week] [, Year])


WeekDay

- Flow: i; data types: Number

The number of the day in the week (1..7). Monday is No. 1.


Week

- Flow: i; data types: Number

The number of the week. If this optional parameter is not used, the default
is the current week.
Year

- Flow: i; data types: Number

Ranges from 1980 to 2059. If this optional parameter is not used, the
default is the current year
Date

- Flow: o; data types: Date

The resulting date.

General Comments...

A special situation occurs if the input Week to DWY2DATE is a week


which overlaps two years. Depending on the Weekday, the year of the
output Date may be different from the input year. This is illustrated by the
example below.

For Example...

The following AL code sample shows how to use the DWY2DATE function. Please note that the input week in this example overlaps two years.
DayOfWeek := 7;
Week := 52;
Year := 1993;
aDate := DWY2DATE(DayOfWeek, Week, Year);
MESSAGE('The %1. day of the week \' +
'in the %2. week\' +
'in the year %3,\' +
'corresponds to the date: %4', DayOfWeek, Week, Year, aDate);

The message box will show:


The 7. day of the week
in the 52. week
in the year 1993,
corresponds to the date: 01/02/94

The example illustrates that the system regards the 7. day of the week in
the 52. week in the year as the date 01/02/94.

122

ALReference Guide

ERROR

ERROR

Prints an error message on the screen and ends the execution of the AL
code.

ERROR(String [, Value1, ...])


String

-Flow: i; data types: String

A string containing text to be shown in the error box. The special character '\' divides String into sub-strings, and causes the printing to start on a
new line.
In order to insert variable values in the string, '%'- or '#'-fields can be
inserted at the position(s) where the value(s) are to be substituted.
The size of the resulting error box is determined as follows: the width corresponds to the longest sub-string in String, and the height corresponds to
the number of sub-strings (lines). The system truncates any sub-string
longer than 64 characters.
Value1,...

- Flow: i; data types: see text below

Any variable or expression to be inserted in String. A maximum of 10


values can be inserted.
Values substituted in a '#'-type field will be truncated to a length corresponding to the number of '#'-characters, whereas values substituted in a
'%'-type field will be printed full length.

For Example...

The following AL code sample illustrates how to use the ERROR function.
AccountNo := 1230;
{The execution will be halted when the error statement is executed,}
{and the following statements will never be executed.}
ERROR('Finance Account #1####\' +
'must not be blocked', AccountNo);
MESSAGE('Dummy message!');
{This line is never executed}

The ERROR box will show:


Finance Account 1230
must not be blocked

... the ERROR statement causes the execution of the AL code to be


halted, that is, the MESSAGE function will never be executed.

AL Reference Guide

123

EVALUATE

EVALUATE

Evaluates a string representation of a value into its normal representation.


The result is assigned to a Variable.

[Ok :=] EVALUATE(Variable, String)


Variable

- Flow: o; data types: Yes/No, Number, Date, Time, String

Any type of variable. The value of the evaluated string is assigned to the
variable.
String

- Flow: i; data types: String

A string containing a value of any simple AL data type.


Ok

- Flow: o; data types: Yes/No

A return code which reflects whether an error occurred during the evaluation of the string.
Ok

Indicates that...

TRUE

No errors occurred during the evaluation of the


string. The value has been assigned to Variable.

FALSE

Errors occurred during the evaluation of the string.


The original value of Variable has not been modified.

If this optional return value is not used, an error during the evaluation of
the string will cause a run-time error. Otherwise it is assumed that the programmer takes care of the error-handling.

For Example...

The following AL code sample illustrates the behavior of the EVALUATE function when called with variables of three different types.
Value := '010196';
Ok1 := EVALUATE(VarInteger, Value);
Ok2 := EVALUATE(VarDate, Value);
Ok3 := EVALUATE(VarYesNo, Value);
MESSAGE('VarInteger = #1######, and the return code is: %2\' +
'VarDate
= #3######, and the return code is: %4\'+
'VarYesNo
= #5######, and the return code is: %6', VarInteger, Ok1, VarDate, Ok2,
VarYesNo, Ok3);

The message box will show:


VarInteger = 10196 , and the return code is: Yes
VarDate
= 01/01/96, and the return code is: Yes
VarYesNo
= No
, and the return code is: No

The example illustrates that Value ('010196') can be interpreted both as


an integer- and a date-expression, while it can not be interpreted as an
Yes/No-expression. This causes an error which is reflected through the
value of the return code Ok3 (=FALSE).

124

ALReference Guide

FORMAT

FORMAT

Formats an input Value, and returns the result as a String.

String := FORMAT(Value [, Length] [, FormatNo])


Value

- Flow: i; data types: see text below

Value denotes an AL variable (expression) of any simple AL data type.


Note: If the formatting results in a value larger than

MAXSTRLEN(String) a run-time error occurs.


Length

- Flow: i; data types: Number

This optional argument is an integer which determines the length of the


resulting string. Observe the following rules:
Value of Length Specifies that...

FormatNo

Length = 0

The entire Value will be output (Default).

Length > 0

String will be exactly Length characters.


If the length of Value is less than Length characters, spaces
will be padded either as leading or trailing characters, depending on which FormatNo you use.
If Value is a Number which exceeds Length digits, the output
string will contain Length asterisks. Any other non-Number
Value which exceeds Length characters, will be truncated.

Length < 0

String will have the maximum length of -Length characters.


If the length of Value is less than -Length characters, the
length of String will be set to the length of Value
If Value is a Number which exceeds -Length digits, the output
string will contain -Length asterisks. Any other non-Number
Value which exceeds -Length characters, will be truncated.

- Flow: i; data types: Number

This optional argument determines the format to be used during the formatting process. There are three or more formats for each simple AL data
type:
Format No.

Explanation

Standard Display Format. Default


for all data types

Standard Display Format 2 (Edit)

AL Code Constant Format

3, 4, ...

Other formats

In the following four subsections we describe all possible formats for any
simple AL data type. The subsections describe formats for:
1. Numbers: Yes/No, Option, Integer, Long Integer, Decimal No.
2. Dates.
3. Times.
4. Strings: Text and Code.

AL Reference Guide

125

FORMAT

Formatting of Numbers
Table 28 shows match characters for Numbers formats. These match characters are used (in column 3) in table 29, which shows the possible formats for Numbers.

Table 28: Match Characters in Number Formats


Sign

Explanation...

Yes/No

Display a Yes with the value 1; display a No with the


value 0.

Display the corresponding text from the option set


defined for the field.

Display a minus with any negative number; display


nothing with any positive number.

-/_

Display a minus with any negative number; display a


space with any positive number.

Display the integer part of the number without thousand separators.

#,###

Display the integer part of the number with thousand


separators.

.d

Display the fractional part of the number, if any, with


a leading period.

Table 29: Number Formats


Data type

FormatNo Match Character Alignment

Example

Yes/No

0, 1

Yes

No

Option1

Option2

2
Option

0, 1
2

Integer

0, 2
1

Long Integer 0
1
2

126

ALReference Guide

Yes/No
#
t
#
-#
-#
-#,###
-#
-#

Left
Left
Left
Left
Right
Left
Right
Left
Right

12345

-12345

12345

-12345

12,345

-12,345

12345

-12345

12345

-12345

FORMAT

Table 29: Number Formats


Data type

FormatNo Match Character Alignment

Example

Decimal No.

12,345.12

-12,345.12

1
2
3
4

-#,###.d
-#.d
-#.d
#,###.d-/_
#.d-/_

Right
Left
Right
Right
Right

12345.12

-12345.12

12345.12

-12345.12

12,345.12

12,345.12-

12345.12

12345.12-

Formatting of Dates
Table 30 shows match characters for Dates. These match characters are
used (in column 3) in table 31, which shows the possible formats for
Dates

Table 30: Match Characters in Date Formats


Sign

Explanation

Display the day as a number without a leading zero


(1-31).

dd

Display the day as a number with a leading zero (0131).

mm

Display the month as a number with a leading zero


(01-12).

mmmm

Display the month as a full month name (JanuaryDecember).

yy

Display the year as a two-digit number (80-99,0059).

yyyy

Display the year as a four-digit number (1980-2059).

Display an uppercase C with any closing date.

D/C

Display an uppercase D with any normal date; display an uppercase C with any closing date.

AL Reference Guide

127

FORMAT

Table 31: Date Formats


Data type Format No. Match character Alignment Example
Date

Cmm/dd/yy

Right

Cmm/dd/yy

Left

06/01/94

06/01/94

mmddyyD/C

Right

060194D

Cyy/mm/dd

Right

94/06/01

mmmm Cd, yyyy Right

June 1, 1994

Cmmddyy

Right

060194

Cyymmdd

Right

940601

Formatting of Times
Table 32 shows match characters for Times. These match characters are
used (in column 3) in table 33, which shows the possible formats for
Times.

Table 32: Match Characters for Time Formats


Sign

Explanation

Display the 12-clock hour as a number without leading zeros (0-11).

hh

Display the 12-clock hour as a number with a leading


space (_0-11).

hh24

Display the 24-clock hour as a number with leading


zeros (00-23).

mm

Display the minute as a number with leading zeros


(00-59).

ss

Display the second as a number with leading zeros


(00-59).

.s

Display the fractional part of second, if any, with a


leading period.

AM/PM

Display an uppercase AM with any hour before noon;


display an uppercase PM with any hour between
noon and 11:59 PM.

Table 33: Time Formats


Data type Format No. Match character
Time

128

ALReference Guide

Alignment Example

hh:mm:ss.s AM/PM Left

3:04:05.06 PM

h:mm:ss.s AM/PM

Left

3:04:05.06 PM

hh24mmss.sT

Left

15:04:05.06T

FORMAT

Formatting of Strings
Table 34 shows match characters for Strings. These match characters are
used (in column 3) in table 35, which shows the possible formats for
Strings.

Table 34: Match Characters


for String Formats
Sign

Explanation

Display as text.

Table 35: String Formats


Data type Format No. Match character Alignment Example
Text

0, 1, 2

Code

0, 2

String

Left

ABC

Left

ABC

Right

Left

ABC

100

100

-Flow: o; data types: String

A string containing the formatted output.

For Example...

This example shows how to use the FORMAT function.


DecNum := -123456.78;
OutStr := FORMAT(DecNum, 15, 3);
MESSAGE('The formatted value: >%1<', OutStr);

The message box will show::


The formatted value: >

123,456.78-<

AL Reference Guide

129

INCSTR

INCSTR

Increments a positive number, or decrements a negative number inside


String by 1 (one).

NewString := INCSTR(String)
String

-Flow: i; data types: String

The input string.


Note: If the string contains more than one number, only the
number closest to the end of the string will be changed.
Example: A10B20 is changed to A10B21
Note: The number 0 (zero) is considered as a positive number, that is, it is increased by 1 (one). Example: A0 is
changed to A1.

NewString

- Flow: o; data types: String

The output string.

General Comments

For Example...

Notice that when the input string contains a number like 99 (which is
increased to 100), the length of the output string will be: LEN(String) + 1.
The following AL code sample illustrates the properties of the INCSTR
function.
Account := 'Account no. 99 does not balance';
NegAccount := 'Account no. 2342 shows a total of -452 $';
MyAccount := 'My bank account shows a total of 0 $';
MESSAGE('The test-strings before INCSTR is called:\' +
'%1\' +
'%2\' +
'%3\', Account, NegAccount, MyAccount);
ResAccou := INCSTR(Account);
ResNegAc := INCSTR(NegAccount);
ResMyAcc := INCSTR(MyAccount);
MESSAGE('The test-strings when INCSTR has been called:\' +
'%1\' +
'%2\' +
'%3', ResAccou, ResNegAc, ResMyAcc);

The first message box will show:


The test-strings before INCSTR is called:
Account no. 99 does not balance.
Account no. 2342 shows a total of -452 $
My bank account shows a total of 0 $

130

ALReference Guide

INCSTR

... while the second message box will show:


The test-strings when INCSTR has been called:
Account no. 100 does not balance.
Account no. 2342 shows a total of -453 $
My bank account shows a total of 1 $

The example shows that if the string contains more than one number then
only the last number will be changed. Furthermore it is seen, that positive
numbers (and zero) are increased, while negative numbers are decreased.

AL Reference Guide

131

INSSTR

INSSTR

Inserts a SubString in String.

NewString := INSSTR(String, SubString, Position)


String

- Flow: i; data types: String

The main input string.


SubString

- Flow: i; data types: String.

A (sub-)string to be inserted in String


Position

- Flow: i; data types: Number

This optional argument determines the position where SubString will be


inserted in String. Position must be greater than or equal to 1.
Note: If Position > STRLEN(String) then NewString will
contain a result which is a concatenation of String and
SubString.

NewString

- Flow: o; data types: String

The resulting string.

For Example...

The following AL code sample illustrates how to insert one string into
another.
Str := 'Press ENTER to continue';
SubString := 'or ESC ';
MESSAGE('The test-string before INSSTR is called:\' +
'>%1<', Str);
NewStr := INSSTR(Str, SubString, 12);
MESSAGE('The resulting string after INSSTR has been called:\' +
'>%1<', NewStr);

The first message-box will show:


The test-string before INSSTR is called:
>Press ENTER to continue<

...while the second message box will show the following:


The resulting string after INSSTR has been called:
>Press ENTER or ESC to continue<

132

ALReference Guide

LOWERCASE

LOWERCASE

Converts all letters in String to lowercase.

NewString := LOWERCASE(String)
String

- Flow: i; data types: String

The string to be converted. Only letters in the range A..Z (and special
national characters) are affected.
NewString

- Flow: o; data types: String

The converted output string.

For Example...

The following AL code sample illustrates how to use the LOWERCASE


function.
Str := 'The Entries are Sorted by Name';
MESSAGE('The test-string before LOWERCASE is called:\' +
'>%1<', Str);
Lower := LOWERCASE(Str);
MESSAGE('The resulting string when LOWERCASE has been called:\' +
'>%1<', Lower);

The first message box will show:


The test-string before LOWERCASE is called:
>The Entries are Sorted by Name<

...while the second message box will show:


The resulting string when LOWERCASE has been called:
>the entries are sorted by name<

AL Reference Guide

133

MAXSTRLEN

MAXSTRLEN

Returns the maximum length, that is the defined length, for a String variable.

Length := MAXSTRLEN(String)
String

- Flow: i; data types: String

The input string variable .


Length

- Flow: o; data types: Number

The maximum length of the input string variable.

For Example...

In this example we assume that City has been defined as a Text type variable, with the maximum length 30.
City := 'Atlanta';
MaxLength :=MAXSTRLEN(City);
Length := STRLEN(City);
MESSAGE('The MAXSTRLEN function returns: %1,\' +
'while the STRLEN function returns: %2',MaxLength, Length);

The message box will show:


The MAXSTRLEN function returns: 30
while the STRLEN function returns: 7

134

ALReference Guide

MESSAGE

MESSAGE

Displays a text string in a message box.

MESSAGE(String [, Value1, ...])


String

- Flow: i; data types: String

A string containing text to be shown in the message box. The special


character '\' divides String into sub-strings, and causes the printing to start
on a new line.
In order to insert variable values in the string, '%'- or '#'-fields can be
inserted at the position(s) where the value(s) are to be substituted.
The size of the resulting message box is determined as follows: the width
corresponds to the longest sub-string in String, and the height corresponds
to the number of sub-strings (lines). The system truncates any sub-string
longer than 64 characters.
Value1, ...

- Flow: i; data types: see text below

Any type of AL variable containing a value to be inserted in the text in


String. A maximum of 10 values can be inserted.
Values substituted in a '#'-type field will be truncated to a length corresponding to the number of '#'-characters, whereas values substituted in a
'%'-type field will be printed full length.

General Comments...

For Example...

Observe the following:

When the system executes a message statement in the AL code it will not
display the message box immediately, but wait until the execution of the AL
code is finished or until the system temporarily stops to wait for user
interaction.

Only a limited amount of messages can be held in memory. The exact number
of messages depends on the sizes of the individual messages. The execution
of the code will be interrupted if the total size of the messages exceeds the
available memory.

The following illustrates how to use the MESSAGE function.


Value := 12345.678;
{The backslash is used as 'newline' character.}
{Strings can be concatenated, using the + operator.}
{Variable values can be inserted in the text by using % and #
fields.}
MESSAGE('The message box can be used to\' +
'display text and numbers:\' +
'Free format:\' +
'>%1<\' +
'Two different fixed formats:\' +
'>#2#############<\' +
'>#3###<', Value, Value, Value);

AL Reference Guide

135

MESSAGE

The message box will show:


The message box can be used to
display text and numbers:
Free format:
>12,345.678<
Two different fixed formats:
>12,345.678
<
>12,34<

Note the way the fixed format truncates the number in the last line above.

136

ALReference Guide

NORMALDATE

NORMALDATE

Returns the normal date (not closing) for the argument Date.

NewDate := NORMALDATE(Date)
Date

- Flow: i; data types: Date

The input date which can be either a closing date or a normal date. If the
value of Date is set to the undefined date (0D), a run time error will occur.
Note: All dates have a corresponding closing date. A closing date is
regarded by the system as a period following the given date, but before
the next normal date, thus closing dates are sorted immediately after the
corresponding normal date, but before the next normal date.

The figure below illustrates how the system sorts the closing dates
between the normal dates for an arbitrary month..

April 1994
040194D 040194C

040294D

040294C 040394D 040394C

...

xxxxxxD: Normal date


xxxxxxC: Closing date

NewDate

- Flow: o; data types: Date

The resulting normal date.


If Date is a...

For Example

Then NewDate will be a...

Normal date

Normal date

Closing date

Normal date

The following AL code samples illustrates how to use the NORMALDATE function. In the first example a normal date is given as input, and
in the second example a closing date is given as input.
A normal date as input.

OldDate := 040494D;
NorDate := NORMALDATE(OldDate);
MESSAGE('The normal date for %1 is %2, OldDate, NorDate);

The message box will show:


The normal date for 04/04/94 is 04/04/94
A closing date as input.

OldDate := CLOSINGDATE(040494D);
NorDate := NORMALDATE(OldDate);
MESSAGE(The normal date for %1 is %2,OldDate, NorDate);

The message box will show:


The normal date for C04/04/94 is 04/04/94

AL Reference Guide

137

OPENWINDOW

OPENWINDOW

Opens a dialog window on the screen

OPENWINDOW(String [, Line] [, Pos])


String

- Flow: i; data types: String

A string containing text to be shown in the window. The special character


'\' divides String into sub-strings, and causes the printing to start on a new
line. The size of the resulting window is determined as follows: the width
corresponds to the longest sub-string in String, and the height corresponds
to the number of sub-strings (lines).
In order to insert variable values in the string, '#'-fields can be inserted at
the positions where the values are to be substituted.
Values substituted in a '#'-type field will be truncated to a length corresponding to the number of '#'-characters.
Values can be updated by UPDATEWINDOW, or edited by the user, by
using WINDOWINPUT.
Line, Pos

- Flow: i; data types: Number

These optional parameters are used to specify the position of the upper
left corner of the window. Line and Pos must be greater than or equal to 1.
If Line and Pos are not specified the window will be centered at the middle of the screen.

General Comments...

For Example...

Each object (function-, report-, ...) can only have one dialog window open at
a time. The system will automatically close an existing dialog window before
it opens a new dialog window.

The system will also close the dialog window automatically when an object
is terminated.

This example shows how to use the OPENWINDOW function.


AccountInfo := 'Account no. #1######,\' +
'shows a total of #2###### $';
AccNo := 5634;
TotSum := 1000;
OPENWINDOW(AccountInfo, 1, 1); {Upper left corner}
{Opens a window with '#'-fields for Account no. and Total}
UPDATEWINDOW(1, AccNo);
{Fill in field no. 1}
UPDATEWINDOW(2, TotSum); {Fill in field no. 2}
WAITKEYPRESS();
CLOSEWINDOW()

The system will open the dialog window and display the following text:
Account no. 5634
shows a total of 1000 $

This shows that the values of the variables AccNo and TotSum has been
inserted in the '#'-fields.

138

ALReference Guide

POWER

POWER

Calculates NumberPower, or in other words, the Powerth power of Number.

NewNumber := POWER(Number, Power)


Number

- Flow: i; data types: Number

Number denotes the base in the exponential function.


Power

-Flow: i; data types: Number

Power denotes the exponent in the exponential function.


NewNumber

- Flow: o; Domains; Decimal No.

The resulting value: NumberPower.

For Example...

First we will present some typical uses of the POWER function, and second we will show an AL code saple illustrting how to use the POWER
function.
Compound Amount

If a principal P is deposited at interest rate r (in decimals) compounded


annually, then at the end of n years the accummulated amount is:

A = P ( 1 + r) n
Example: A man deposits $2800 in a bank which pays 5% compounded
quarterly. What will the deposit amount to in 8 years?
Answer: There are n = 8x4 = 32 payment periods at interest rate
r = 0.05/4 = 0.0125 per period. Then the amount is:
A = $2800(1 + 0.0125)32 =$2800(1.4881) = $4166.68
The Amount of an Annuity

If a principal P is deposited at the end of each year at interest rate r (in


decimals) compounded annually, then at the end of n years the accumulated amount is:

( 1 + r) n 1
A = P ----------------------------r
This is often called an annuity.
Example: An investor has an annuity in which a payment of $500 is made
at the end of each year. If interest is 4% compounded annually, what is the
amount of the annuity after 20 years?
Answer: Here r = 0.04, n = 20 and the amount will be:
A = $500((1 + 0.04)20 - 1)/0.04 = $14,889.05

AL Reference Guide

139

POWER

An AL Code Sample

Number1 := 2;
Power1 := 8;
Number2 := 100; Power2 := 0;
Number3 := 5;
Power3 := -0.5;
Res1 := POWER(Number1, Power1);
Res2 := POWER(Number2, Power2);
Res3 := POWER(Number3, Power3);
MESSAGE('%1 raised to the power of %2 = %3', Number1, Power1, Res1);
MESSAGE('%1 raised to the power of %2 = %3', Number2, Power2, Res2);
MESSAGE('%1 raised to the power of %2 = %3', Number3, Power3, Res3);

The message boxes will show:


2 raised to the power of 8 = 256
100 raised to the power 0 = 1
5 raised to the power of -0.5 = 0.44721359549994

The last message box shows that raising a number to the power of -0.5
corresponds to the squareroot of the number.

140

ALReference Guide

ROUND

ROUND

Rounds off the value of a Number variable.

NewNumber:= ROUND(Number [, Precision] [, Direction])


Number

- Flow: i; data types: Decimal No.

The input value to be rounded off.


Precision

- Flow: i; data types: Decimal No.

This optional parameter determines the precision used when rounding off.
The default value is 0.01 for the US version. (In some european countries
other default values may be used).
Direction

- Flow: i; data types: String

This optional parameter determines how Number is rounded off. The


valid values are one of the following three strings:
Value of Direction Causes the function to...

NewNumber

'='

round off to the nearest value.


(Default value)

'>'

round up.

'<'

round down.

- Flow: o; data types: Decimal No.

The rounded output value.

For Example...

The following example shows how to use the ROUND function.


DN := 1234.56789;
Larger := >;
Precision := 0.001;
Res := ROUND(DN, Precision, Larger);
MESSAGE(ROUND(%1, %2, %3) returns %4,DN, Precision, Larger,Res);

The message box will show:


ROUND(1,234.56789, 0.001, >) returns 1,234.568

The table below provides additional ROUND examples:

AL Reference Guide

141

ROUND

Number

1234.56789

-1234.56789

142

ALReference Guide

Precision

Direction

NewNumber

100

1200

0.1

1234.6

0.001

1234.568

0.001

<

1234.567

0.001

>

1234.568

100

-1200

0.1

-1234.6

0.001

-1234.568

0.001

<

-1234.567

0.001

>

-1234.568

SELECTSTR

SELECTSTR

Retrieves a sub-string from a comma separated string.

NewString := SELECTSTR(Number, CommaString)


Number

- Flow: i; data types: Number

A number determining the sub-string to be retrieved. The sub-strings in


the comma separated string are enumerated as 1, 2, 3, ...
Note: If Number is greater than the number of sub-strings, a
run-time error will occur.

CommaString

- Flow: i; data types: String

A string containing sub-strings separated by commas.


NewString

- Flow: o; data types: String

The sub-string retrieved from CommaString.

For Example...

The following AL code sample shows how to use the SELECTSTR function.
CommaStr := 'This,is a comma,separated,string';
SubStr1 := SELECTSTR(2, CommaStr); {Pick out the 2nd substring}
SubStr2 := SELECTSTR(4, CommaStr); {Pick out the 4th substring}
MESSAGE('The two calls to SELECTSTR returns:\' +
'>%1<\' +
'>%2<', SubStr1, SubStr2);

The message box will show the following:


The two calls to SELECTSTR returns:
>is a comma<
>string<

AL Reference Guide

143

SERIALNO

SERIALNO

Returns a string, which contain the serial-number for the licence file for
your program.

String := SERIALNO()
String

- Flow: o; data types: String

An output string describing the serial number.

For Example...

The following AL code sample shows how to use the SERIALNO function.
Sn := SERIALNUMBER();
MESSAGE('The serial number for this\' +
'software package, is: %1', Sn);

The serial number is of course dependent of your software package. The


serial number shown in the message box is just an example::
The serial number for this
software package, is: 1-N3001234

144

ALReference Guide

SETSTRLEN

SETSTRLEN

Changes the length of a String to a specific Length, either by truncating it,


or by concatenating FillerCharacters at the end.
NewString := SETSTRLEN(String, Length[, FillerCharacter])

String

- Flow: i; data types: String

The input string.


Length

-Flow: i; data types: Number

A number describing the desired length of the output string.


If Length is less than STRLEN(String), the string is truncated. Otherwise
it is expanded with filler characters.
FillerCharacter

- Flow: i; data types: String

This is a string of the length 1. The character in this string is used to 'fill
out' empty space at the end of the output string.
If not specified, blanks are used as default.
NewString

- Flow: i; data types: String

The output string. The length of NewString is Length characters.


Note: If the maximum (defined) length of the NewString is
less than Length, a run-time error will occur.

For Example...

The following AL code sample illustrates how to use the SETSTRLEN


function.
Str1 := '13 characters';
Str2 := 'Four';
Len1 := STRLEN(Str1);
Len2 := STRLEN(Str2);
MESSAGE('Before SETSTRLEN has been called:\' +
'
>%1<, has the length %2\' +
'
>%3<, has the length %4\', Str1, Len1, Str2, Len2);
Str1 := SETSTRLEN(Str1, 5);
{Truncate the length to 5}
Str2 := SETSTRLEN(Str2, 15, 'w');{Concatenate w's until length = 15}
Len1 := STRLEN(Str1);
Len2 := STRLEN(Str2);
MESSAGE('After SETSTRLEN has been called:\' +
'
>%1<, has the length %2\' +
'
>%3<, has th length %4\', Str1, Len1, Str2, Len2);

The first message box will show::


Before SETSTRLEN has been called:
>13 characters<, has the length 13
>Four<, has the length 4

AL Reference Guide

145

SETSTRLEN

...while the second message box will show:


After SETSTRLEN has been called:
>13 ch<, has the length 5
>Fourwwwwwwwwwww<, has the length 15

146

ALReference Guide

STRCHECKSUM

STRCHECKSUM

Calculates a checksum for a string containing a number.


CheckNumber:=STRCHECKSUM(String [, WeightString] [, Modulus])

String

- Flow: i; data types: String

The input string.


Note: Only the numeric characters '0'...'9' are allowed in this
string. Using other characters will cause a run time error.

WeightString

- Flow: i; data types: String

A string containing numbers to be used as weights when calculating the


checksum. The default value is a string containing STRLEN(String)
1-characters.
Note: Only the numeric characters '0'...'9' are allowed in this
string. Using other characters will cause a run time error.
Note: If String is longer than WeightString, then a string
containing STRLEN(String) - STRLEN(WeightString) 1characters will be concatenated to the end of WeightString. If
WeightString is longer than String a run time error will occur.

Modulus

- Flow: i; data types: Number

A number to be used in the checksum formula (see below).


The default value is 10.
CheckNumber

- Flow: o; data types: Number

The checksum, which is calculated according to the following formula.

M o d u l u s Stri ng [ i ] W e i g h t S t r i n g [ i ] MOD

Mo dulu s MOD

Mo dulu s

where n = STRLEN(String).

For Example...

The following AL code sample shows how to use the STRCHECKSUM


function.
Calculating a checksum:

StrNumber := '4378';
Weight := '1234';
Modulus := 7;
CheckSum := STRCHECKSUM(StrNumber, Weight, Modulus);
MESSAGE('The number: %1\' +
'has the checksum: %2', StrNumber, CheckSum);

The STRCHECKSUM('4378','1234', 7) returns:

AL Reference Guide

147

STRCHECKSUM

(7 - (4x1 + 3x2 + 7x3 + 8x4) MOD 7) MOD 7 = 0


...therefore the message box will show:
The number: 4378
has the checksum: 0
Calculating a modulus 10 checksum for bar code:

The STRCHECKSUM function can be used to calculate checksums for


13 and 8 digit EAN (European Article Number) and EAN compatible bar
codes such as UPC (Universal Product Code) or JAN (Japanese Article
Number).
An 13 digit EAN code has the following format:
Position: 13
5

12
7

11
7

10
6

Country code

9
2

8
2

7
1

6
3

5
5

4
7

3
4

2
6

1
3

Checksum

Method:
1. The 12 digits in position 13 to 2 are used to calculate the checksum at position
1.
2. Starting from position 2 all values at even positions are summed. The result is
multiplied by three. We call this value Even.
3. Starting from position 3 all values at odd positions are summed. We call this
value Odd.
4. Total = Even + Odd.
5. The modulus 10 checksum is then: (10 - Total MOD 10) MOD 10.

The following shows how to use STRCHECKSUM to calculate this


result.
StrNumber := 577622135746;
Weight :=
131313131313;
CheckSum := STRCHECKSUM(StrNumber, Weight);
MESSAGE(The EAN code: %1\ +
has the checksum: %2, StrNumber, CheckSum);

The message box will show:


The EAN code: 577622135746
has the checksum: 3

148

ALReference Guide

STRLEN

STRLEN

Returns the length of the input string.

Length := STRLEN(String)
String

- Flow: i; Data types: String

The input string.


Length

- Flow: o; data types: Number

The length of String.

General Comments...

The difference between the STRLEN and MAXSTRLEN functions is that


the first reflects the actual number of characters in the input string, while
the latter reflects the defined maximum length for the input string. Refer
to the example below.

For Example...

The following AL code sample illustrates the difference between the


STRLEN and the MAXSTRLEN functions. We assume that the variable
City has been defined with the maximum length 30.
City := 'Atlanta';
MaxLength :=MAXSTRLEN(City);
Length := STRLEN(City);
MESSAGE('The MAXSTRLEN function returns: %1,\' +
'while the STRLEN function returns: %2',MaxLength, Length);

The message box will show:


The MAXSTRLEN function returns: 30
while the STRLEN function returns: 7

This illustrates that the MAXLENGTH function returns the maximum


possible length according to the definition of the string variable, while
STRLEN returns the actual length of the text.

AL Reference Guide

149

STRMENU

STRMENU

Creates a menu box. The menu box is placed at the middle of the screen.

OptionNo := STRMENU(OptionString [, DefaultNo])


OptionString

- Flow: i; Data types: String

A comma separated string. Each sub-string in OptionString denotes an


option in the menu box.
DefaultNo

- Flow: i; data types: Number

This optional parameter determines a default option, by highlighting one


of the options in the menu box. The options in the menu box are enummerated 1 , 2 , 3...
If DefaultNo is not specified, the default no. is 1 (one).
OptionNo

- Flow: o; data types: Number

The selected menu option. If the user used ESC to quit the menu box, the
value 0 (zero) will be returned.

For Example...

The following AL code sample illustrateshow to use the STRMENU


function.
Options := 'Save,Delete,Exit to OS,Find ...';
Selected := STRMENU(Options, 3); {The 2nd paramenter sets}
{the default to option no. 3}
MESSAGE('You selected option no.: %1', Selected);

A menu box with the following options will be shown:


Save
Delete
Exit To OS
Find ...

...and the cursor will be placed on option no. 3 ('Exit to OS'). The selected
option will be stored in the variable Selected, and shown by the message
box as, for example:
You selected option no.: 2

150

ALReference Guide

STRPOS

STRPOS

Searches for a SubString in a String.

Position := STRPOS(String, SubString)


String

- Flow: i; data types: String

The string to be searched in.


SubString

- Flow: i; data types: String

The string to be searched for.


Note: The STRPOS function is case-sensitive.

Position

- Flow: o; data types: Number

The position of SubString in String if SubString was found. If the search


fails, the value 0 will be returned.

General Comments...

The STRPOS function only returns the position of the first occurrence of
the sub-string.

For Example...

The following AL code sample illustrates how to use the STRPOS function.
String := 'ABC abc abc xy';
SubStr := 'abc';
Pos := STRPOS(String, SubStr);
MESSAGE('The search for the substring: >%1<\' +
'in the string: >%2<,\' +
'returns the position: %3', SubStr, String, Pos);
{The STRPOS function is case sensitive. Furthermore it only}
{returns the position of the first occurrence of the substring.}

The message box will show::


The search for the substring: >abc<
in the string: >ABC abc abc xy<
returns the position: 5

AL Reference Guide

151

STRSUBSTNO

STRSUBSTNO

Replaces %1, %2,... and #1, #2, ... fields in a String with the value(s)
given as optional parameter(s).

NewString := STRSUBSTNO(String [,Value1, ...])


String

- Flow: i; data types: String

A string containing '#' and/or '%' fields.


The % fields are replaced by the values in full lengths, while # fields are
replaced by the values truncated to the length of the # field
Value1, ...

- Flow: i; data types: -

One or more values (expressions) to be inserted in String. The value(s)


can be of any simple AL datatype.
Note: A maximum of 10 Values can be specified.

NewString

- Flow: o; data types: String

An output string in which the '#' and '%' fields have been substituted with
the corresponding values.

For Example...

The following AL code sample shows how to use the STRSUBSTNO


function.
Str := 'The balance of account %1 is #2######## $';
AccountNo := 3452;
MESSAGE('The string before STRSUBSTNO has been called:\' +
'%1', Str);
Str := STRSUBSTNO(Str, AccountNo, 2345 + 5462);
MESSAGE('The string after STRSUBSTNO has been called:\' +
'%1', Str);

The first message box will show:


The string before STRSUBSTNO has been called:
The balance of account %1 is #2######## $

...while the second message box will show:


The string after STRSUBSTNO has been called:
The balance of account 3452 is 7,807
$

152

ALReference Guide

SYSTEMVERSION

SYSTEMVERSION

Returns a String, which contain the name and version of the operating
system.

String := SYSTEMVERSION()
String

- Flow: o; data types: String

A string describing the type and version of the operating system. Some
typical examples of the format of the returned string are:

For Example...

Operating System

String

DOS version 6.0

DOS_6.0

AIX version 3.2

AIX_3.2

HP UX version 8.02

HP-UX_8.02

OS/2 version 2.1

OS/2_2.1

This example shows how to use the SYSTEMVERSION function.


Ver := SYSTEMVERSION();
MESSAGE('The operating system is: %1', Ver);

A typical result:
The operating system is: DOS_6.0

AL Reference Guide

153

TIME

TIME

Returns the current time set in the operating system.

Time := TIME()
Time

- Flow: o; data types: Time

The current time.

General Comments...

For Example...

The TIME function can only be used to retrieve the time from the operating system, that is, you cannot use the TIME function to set the time in
the operating system.
The following AL code sample shows how to use the TIME function.
CurrentTime := TIME(); {Get the current time
from the operating system.}
MESSAGE('The current system time is: %1', CurrentTime);

A typical result:
The current system time is: 4:15:46.84 PM

154

ALReference Guide

TODAY

TODAY

Returns the current date set in the operating system.

Date := TODAY()
Date

- Flow: o; data types: Date

The current date.

General Comments...

For Example...

The TODAY function can only be used to retrieve the current date from
the operating system, that is, you cannot use the TODAY function to set
the date in the operating system.
The following AL code illustrates how to use the TODAY function.
CurrentDate := TODAY();
MESSAGE('The current date is: %1', CurrentDate);

A typical result:
The current date is: 05/27/94

AL Reference Guide

155

UPDATEWINDOW

UPDATEWINDOW

Updates the Value of a #-field in the window which is currently open.

UPDATEWINDOW(Number, Value)
Number

- Flow: io; data types: Number

All #-fields have a specific number. The Number argument is used to


determine which field Value will be inserted to in the current window.
Value

-Flow: i; data types: -

The Value (or expression) can be of any simple AL datatype.

For Example...

The following example shows how to update #-fields in a window.


AccountInfo := 'Account no. #1######,\' +
'shows a total of #2###### $';
AccNo := 5634;
TotSum := 1000;
OPENWINDOW(AccountInfo, 1, 1);
{Opens a window with '#'-fields for Account no. and Total}
UPDATEWINDOW(1, AccNo);
{Fill in field no. 1}
UPDATEWINDOW(2, TotSum); {Fill in field no. 2}
WAITKEYPRESS();
CLOSEWINDOW()

The system will display the following window:


Account no. 5634
shows a total of 1000

The values of the variables AccNo and TotSum has been inserted in the
'#'-fields.

156

ALReference Guide

UPPERCASE

UPPERCASE

Converts the letters in a String to uppercase.

NewString := UPPERCASE(String)
String

- Flow: i; data types: String

The string to be converted.


NewString

- Flow: o; data types: String

The converted string.

For Example...

The following AL code sample illustrates how to use the UPPERCASE


function.
Lower := 'Outstanding Order Status';
MESSAGE('The test-string before UPPERCASE is called:\' +
'%1', Lower);
Upper := UPPERCASE(Lower);
MESSAGE('The resulting string when UPPERCASE has been called:\' +
'%1', Upper);

The first message box will show:


The test-string before UPPERCASE is called:
Outstanding Order Status

...while the second message box will show:


The resulting string when UPPERCASE has been called:
OUTSTANDING ORDER STATUS

AL Reference Guide

157

USERID

USERID

Returns the current user-ID.

String := USERID()
String

- Flow: o; data types: String

A string which describes the ID for the current user.

General Comments...

When you enter your application you will be prompted for your user-ID.
The USERID function reflects this input.

For Example...

The following AL code sample illustrates how to use the USERID function.
User := USERID();
MESSAGE('The system was started by %1', User);

A typical result:
The system was started by: JOE

158

ALReference Guide

WAITKEYPRESS

WAITKEYPRESS

Pauses execution of the AL code until Enter or ESC is pressed.

WAITKEYPRESS()
General Comments...
For Example...

The system will ignore if you press any other key than Enter or ESC.
This AL code sample illustrates how to use the WAITKEYPRESS function.
Str:= 'An arbitrary message to the user\' +
'Press ENTER or ESC to continue';
OPENWINDOW(Str);
WAITKEYPRESS(); {Pauses until ENTER or ESC is pressed}
CLOSEWINDOW();

The above AL code will display the following:


An arbitrary message to the user
Press ENTER or ESC to continue

If the execution of the AL code was not paused by WAITKEYPRESS, the


user would not be able to read the text in the window, as the system would
close the window immediately.

AL Reference Guide

159

WINDOWINPUT

WINDOWINPUT

Reads user input in a field (identified by a Number) in a dialog window.


The user input is returned in a Variable. The function also returns a
NewNumber which identifies the next input field in the dialog window.

NewNumber := WINDOWINPUT(Number, Variable)


Number

- Flow: i; data types: Number

A number which identifies a field in the dialog window.


Variable

- Flow: io; data types: -

A variable that may be edited in the dialog window. All AL datatypes are
valid.
NewNumber

- Flow: o; data types: Number

The number of the next field in the window. Please observe that if the
ESC key is used to leave the window, a 0 will be returned instead.

For Example...

This example shows how to use the WINDOWINPUT function.


OPENWINDOW('
Activities\' +
' Main
Sub
Part\' +
' #1######## #2######## #3######## ');
UPDATEWINDOW(1,"MainAct.Code");
UPDATEWINDOW(2,"SubAct.Code");
UPDATEWINDOW(3,"PartAct.Code");
i := 1;
REPEAT
CASE i OF
1 : i := WINDOWINPUT(1,"MainAct.Code");
2 : i := WINDOWINPUT(2,"SubAct.Code");
3 : i := WINDOWINPUT(3,"PartAct.Code");
END;
UNTIL i = 0;
CLOSEWINDOW();

First a dialog window is opened at the middle of the screen. Then a


REPEAT-UNTIL loop makes it possible to move between the 3 input
fields using the arrow keys or Enter. You quit the loop by pressing Esc.
Finally the window is closed.

160

ALReference Guide

WORKDATE

WORKDATE

Returns the work date.

WorkDate := WORKDATE()
WorkDate

- Flow: o; data types: Date

The work date.

General Comments...

For Example...

This function returns the work date chosen using the Work Date option on
the General pull-down menu. If the user has not specified a work date,
then this function returns the current system date.
The following AL code sample illustrates how to use the WORKDATE
function.
WorkDate := WORKDATE();
MESSAGE('The work date is: %1', WorkDate);

A typical response could be:


The workdate is: 05/27/94

AL Reference Guide

161

WORKDATE

162

ALReference Guide

Part

3 The NAVISION DBMS


NAVISION is a comprehensive software system comprised of standard,
functional modules. It enables simple and speedy registration - followed by
control and maintenance - of financial and resource information.
The system provides a set of tools with which you can organize and
manipulate data in an intuitive, efficient manner. The effectiveness of these
tools, however, can be fully realized only by a user with a clear
understanding of the underlying concepts of database management. This
manual introduces the concepts necessary for the use of the NAVISION
Development System.
All operations on the database are performed by a Database Management
System (DBMS). The DBMS is a general purpose software system which
supports processes of creating, maintaining and manipulating databases for
your applications. Common to both single- and multi-user configurations of
NAVISION is that the DBMS allows you to:
Create, delete and expand databases.
Create and delete tables.
Create, delete and change field and key definitions in tables.
Access database status information.
Access information about properties of tables, fields and keys.
The DBMS can also be controlled through various programming interfaces,
such as:
The NAVISION Application Language (AL). A fourth-generation
language which permits developers to control the DBMS via the
NAVISION Development System.
The NAVISION C-Toolkit interface. A C programming interface which
permits developers to control the DBMS from their own C programs.

The NAVISION DBMS

163

The Aim of This Document


Figure 1 gives an overview of the main elements in a NAVISION system.
The figure illustrates clearly that the DBMS is the main component as it links
your NAVISION applications or C programs to the database. This document
will zoom in on the individual parts of this figure, and discuss the internal
elements.
Figure 1. Overview
of the NAVISION system.

MYPROG.EXE
via C-library

NAVISION
via AL code

Database
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Interface
Database Management System

DBMS

Database

c:

d:

f:

The following sections will discuss the following topics, based on figure 1:
The first section introduces you to the terms Physical and Logical
database. The main concern in this section is the structure of the Logical
database.
The second section describes the facilities provided by the Database
Management System (DBMS) to ensure the integrity of the data and
restrict access to the database.
The third section introduces the NAVISION program interface. This
interface enables you to access any subset of records in a NAVISION
database, in a specific sorting.
The fourth section presents special features designed in order to increase
the run time performance of NAVISION.

164

The NAVISION DBMS

The Structure of the Database

The Structure
of the Database

If you are new to the NAVISION database system, this section will introduce
you to some of the most important concepts you need to understand in order
to work with data in a NAVISION database. The word database is in such
common use that we must begin by defining what a database is.
We normally consider a database as a collection of related information, or
data. By data we mean some known facts that can be stored and that have an
implicit meaning. A typical example is a list which describe the names,
addresses and telephone numbers of your customers. These data can be
recorded in an indexed address book, or in a disk file using appropriate
software. This is a simple example of a collection of related information and
hence it is a database.
In this document we will consider a database as a collection of related data
which is stored in a computer in a way that enables you to retrive the
information you need very quickly.

Physical vs. Logical Database


As a typical database user you are not concerned with where each data item is
stored on the hard disk or what its size is; you are only concerned that when a
reference is made to for example a Name, the correct value is returned.
This is why the NAVISION database system provides you with a conceptual
representation of data that does not include to many details of how the data is
stored. An abstract data model is used to provide this conceptual
representation. This data model uses logical concepts, such as objects, their
properties and their relations, which are easier to understand
This leads us to distinguish between the Logical and the Physical database.
When we speak about the logical database we are only concerned with the
structures of the data and the relationships between different bits of
information, that is, in this description we do not deal with how these
structures and relations are implemented. When we speak about the physical
database we only deal with how the structures in the logical database and the
search paths between them are implemented.
When we use the term database in the following, it should be interpreted as
the logical database.
What the user sees as a coherent set of information in the NAVISION
database system can be stored on up to 16 physical disk files, but this is
transparent to the user.
Figure 2 illustrates how one logical database physically can be stored on
three hard disks d:, e: and f:, but nevertheless they still comprise a single
(logical) database.
The logical database contains Objects. These objects can be divided into two
main categories:
1. Object used by a NAVISION application, that is: Batch, Function, Import,
Menu, Report and Window objects
2. Tables
The first category of objects can be seen as basic building blocks providing
services needed when we want to insert, modify, delete or show data stored in

The NAVISION DBMS

165

The Structure of the Database

the other category of objects, that is, the tables. A typical example of the use
of a service provided by the first category of objects, is when we use a
window object to show a detailed view of the fields in a record. Another
typical use of the first category of objects, is when we use a report object to
print, for example, a survey of a budget.
In the rest of this chapter our main concern will be the table objects, that is,
we will discuss the logical structures of a NAVISION database.
Figure 2. The physical
database vs. the logical database

d:\database

e:\database

f:\database

Up to 16 physical disk files


One logical database

Logical Organization
Access to the data is made possible by a well-defined logical organization
composed of:
1. Fields. This is the smallest logical structure used in the NAVISION database. A field is used to hold a single bit of information, for example a
name Joe or an amount 2,352.00. A field can only hold information of
a specific type. The NAVISION database system distinguishes between 9
different types of information. Fields are assembled into a structure called
a Record.
2. Records. This is a logical structure used to store a single entry in the
database. A record consists of a number of fields which are used to store
information about important properties of the entry. Records are organised
in tables.
3. Tables. A table can be thought of as a n times m matrix. Each of the n
rows describe a Record and each of the m columns describe a Field in the
record. Tables are organized in Companies.
4. Companies. This is the largest logical structure used in a NAVISION
database. A Company may be considered as a sub-database, and its primary use is to separate and group large portions of data in a database. A
Company can contain both private tables, and tables shared with other
companies.
These logical units will be discussed in the following sections in the same
order as in the above list.

166

The NAVISION DBMS

The Structure of the Database

Fields and Records


A field can be considered as a single memory cell capable of holding one
single bit of information. In the NAVISION database system 9 different types
of fields can be used. Each type is designed to hold a specific kind of
information, such as Text, Numbers, Dates and so on. Fields in a record can
be one of the following types:
Yes/No. A so-called Boolean data type. Assumes the values TRUE or
FALSE, and fills one byte. When formatted, a Boolean field is shown as
"Yes" or "No".
Option. Denotes an integer in the range 0 to 255. An Option field fills one
byte. An Option type field is defined with an option string, which is a
comma-separated list of strings representing each valid value of the field.
This string is used when a field of type Option is formatted and its value is
converted into a string. An example:

The Option field "Color" is defined with the option string "Red,Green,Blue".
Valid values of the field are then 0, 1 and 2, with 0 representing "Red", etc.
When the "Color" field is formatted, 0 is converted into the string "Red", 1
into "Green", and 2 into "Blue".
Integer. Denotes integers between -32,767 and 32,767 and fills two bytes.
Long Integer. Denotes an integer between -2,147,483,647 and
2,147,483,647 and fills four bytes.
Decimal No. A decimal number between -1063 and 1063. The exponent
ranges from -63 to +63. Decimal numbers are held in memory with 18
significant digits. The representation of a decimal number is a Binary Coded
Decimal (BCD). A field of the type Decimal No. fills 10 bytes.
Date. Contains a date value in the range from January 1,1980 to December
31, 2059. An undefined date is expressed as 0. A field of the type Date is
stored as an Integer, that is, fills two bytes.
Note: All dates have a corresponding closing date. The closing
date for a given date is regarded by the system as a period
following the given date, but before the next normal date; that is,
a closing date is sorted immediately after the corresponding
normal date, but before the next normal date.

A date value for a normal date is calculated as:


NormalDateValue = 1 + (number of days since 1-1-1980) * 2
That is, all normal dates have odd values. The closing dates are sorted
between the normal dates:
ClosingDateValue = 2 + (number of days since 1-1-1980) * 2
That is, all closing dates have even values.
The above rules for calculation of normal and closing dates are exemplified
in the following table.

The NAVISION DBMS

167

The Structure of the Database

Date

No. of days since 1-1-1980

Resulting date value

Undefined

January 1, 1980

January 1, 1980 (closing)

January 2, 1980

January 2, 1980 (closing)

January 3, 1980

January 3, 1980 (closing)

Time. A type Time contains a value of 1 plus the number of milliseconds


since 00:00:00 o'clock, or 0. Time = 0 is an undefined time. A field of type
Time is stored as a Long Integer (fills four bytes). A time value is calculated
in the following way:
Time = 1 + (number of milliseconds since 00:00:00)
Text. Any alphanumeric string. The field must be defined to be between 1
and 80 characters. The space used by a field of type Text equals the
maximum length of the text plus one byte. This extra byte is a used to hold
the length of the string. An empty text string has the length zero.
Code. A type code is an alphanumeric string which is right- justified if the
contents are numbers only. If letters or blanks occur among the numbers, the
contents are left- justified. All letters are converted to uppercase upon entry.
The field must be defined to be between 1 and 80 characters. The space used
by a field of type Code equals the maximum length of the text plus two bytes.
The first of the extra bytes hold information about the length of the string,
and the second byte stores alignment information.

Designing Fields and Tables


Designing a field means assigning it a number of characteristics. These
include: its size, the nature of the data it shall hold, its function in the
database, security level, etc. Other attributes pertaining to a field are the
default values assigned upon record initialization, and the means by which
the field can be manipulated. An Integer field, for example, contains only
numerical values, and can be used in calculations.
On its own a field is not very useful as it only can hold a limited amount of
information. By assembling these small bits of information into Records we
get a much more flexible information-holder. A Record denotes a structure
assembled from an arbitrary number of fields. Figure 3 illustrates a record
which has 4 fields of different type. Each field occupies a number of bytes as
explained in the previous section Fields and Records on page 168.

168

The NAVISION DBMS

The Structure of the Database

Figure 3. A
record shown as
a single row in a
table

A Record with Four Fields


Field
no.:

Field
name:

Number

Name

Amount

660203

Jensen

1267.57

Type:

Long
Integer

Text

Decimal
No.

Size:
(bytes)

Max length + 1

4
Modification
date
10/14/96
Date

10

Field and Table design takes place in the NAVISION development system.
Each field and each table created receives two forms of identification:
The developer defines an identification number (Integer). This number is
a unique identification which cannot be changed. This number is used by
developers working with one of the programming interfaces.
The developer also defines a name, which is an alphanumeric string
serving as a label (such as CUSTOMER or CITY). The name appears in
the user interface (on the screen) and should be meaningful and easily
understood. This name serves as secondary information, and can be
changed at any time.
Besides the ordinary fields discussed in this section, the NAVISION database
system also includes 2 special types of fields:
Calculated-fields
Calc-filter fields
How these special fields provide powerful data manipulation mechanisms is
described in the section Special Database Fields on page 177.

How the DBMS Keeps Track of Fields and Records


The DBMS keeps track of each field by means of a pair of identifying
numbers: the field number which was described above, and the record's
primary key.
The primary key is composed of up to ten fields in a record. The value in this
field(s) must contain a value which makes it possible for the DBMS to
perform a unique identification of each record. This value determines the
logical order in which records are stored, regardless of their physical
placement on disk.
Logically, the records are stored sequentially in ascending order, sorted
according to the value in the primary key field(s). Before adding a new
record to a table, the DBMS checks that the information in these fields is
unique, and only then inserts the record into its correct logical position. By
sorting the records on the fly the database will always be structurally
correct. This enables fast data manipulation and retrieval.

The NAVISION DBMS

169

The Structure of the Database

Tables
The records in the NAVISION database are organized into Tables. A
NAVISION table may be visualized as a two-dimensional matrix, consisting
of columns and rows. A table can hold any number of records with a unique
primary key, each record consisting of up to 255 fields. However, the total
size of a record is limited to 1000 bytes.
Figure 4 shows a table with 6 rows and 5 columns. Each row is a record, and
each column is a field..
Figure 4. The
structure of a
table: Each column denotes a
field, and each
row denotes a
record.

Rows: Records

A Table
Consec.
No.

Account
No.

Date

Text

Amount

Columns: Fields
A table is considered as consisting of two parts: a table description and the
table data. The table data is the part users often think of as comprising the
database, because it contains the actual records with their data fields. The
layout and properties of those fields, however, are specified by the table
description. Figure 5 gives an overview of the major elements in a table
description.
Figure 5. The elements of a table
description.

Table Description
Field
description
- Field Type,
- Name,
- Number,
- Initial Value,
- and other
field properties

Key
description

Other table
properties

- Key field numbers

The table description is used by the DBMS and occasionally by database


users who need information about the database structure. You can inspect the
table description of any table via NAVISION.
The use of table descriptions makes the DBMS flexible, as it enables the

170

The NAVISION DBMS

The Structure of the Database

system to access tables with different structures. When accessing a table the
DBMS can extract the definitions of the table structure from the table
description, and thereby correctly access any table.
An important feature of a table description is that the definition of table
properties can be used by more than one data table. This is discussed in the
section Companies on page 176.
A more detailed description of the contents of the table description is given in
the list below. Each table description contains the following information:
Table number and name. The table number serves as a unique
identification of a specific table, and cannot be changed once it has been
defined. The table name is secondary information, which will be
displayed in the user interface. The name can be changed at any time.
Field Properties. For each field, the following properties are described:
- Type: the nine NAVISION field types described at page 168.
- Number: an integer assigned by the developer during table definition
- Name: an alphanumeric string assigned by the developer during table
definition.
- Initial value: the value assigned to the field upon record initialization
- Formatting properties: how the contents of the field are to be displayed
on screen and when printed.
- Keying limitations: such as, minimum and maximum values, positive
and not blank.
- Entry Procedures: Special tests, adjustments and conversions to be
made to the contents of the fields after keying in.
- Relationships: Another table/other tables with which the field will share
information.
- State: active or inactive. The state is defined upon table creation, and
can be toggled back and forth according to the following simple rule:
An inactive field can always be changed to an active field. But an
active field can only be changed to inactive if the field is unused
(cleared) in all records. This means that if this field contains data in one
or more records, this data will have to be cleared before the field can be
marked as inactive.
A list of Keys:
- Key field numbers.
- Sum fields defined for each key (see appendix A).
AL Code. Special tests, adjustments and conversions to be made when
inserting, modifying or deleting from the table.
The list above shows that the table description includes information about the
key field numbers. At page 170 we have already briefly discussed the notion
of a key; but now that we have introduced you to NAVISION tables, you are
ready to learn more about keys.

Keys
A table description contains a list of keys. A key is a sequence of one or more
field IDs from the table. Up to 20 keys can be associated to a table. The first

The NAVISION DBMS

171

The Structure of the Database

key in the list is the primary key.


The primary key is always active; the DBMS keeps the table sorted in
primary key order and rejects records with duplicate values in primary key
fields. Therefore, the values in the primary key must always be unique. Be
aware that it is not the value in each field in the primary key that must be
unique, but rather the combination of all the fields comprising the primary
key.
A maximum of 10 distinct fields can be used in the definition of the primary
key. The number of fields in the primary key puts a limitation on the number
of fields in the other (secondary) keys.
Some other database systems support unkeyed tables. An unkeyed table is
one for which no key fields have been designated, and records are stored in
the order in which they were entered in the table. The NAVISION database
system does not support unkeyed tables.

Secondary keys
We have already mentioned that up to 20 keys can be associated to a table
and that the first is the primary key. All other keys are secondary keys and
optional. Secondary keys are used when you want to view records in an order
different from the order they are sorted according to the primary key fields.
The number of fields in the primary key affects the possible maximum
number of fields in the secondary keys.
Note: The number of fields in the primary key + the number of
fields in a secondary key which do not occur in the primary key,
must always be less than or equal to ten.
This means that if your primary key includes four distinct fields, then your
secondary keys can include these four fields, and at most six others.
Correspondingly, if your primary key consists of ten distinct fields, then your
secondary keys can only consist of combinations of these fields.
A secondary key uses an additional data structure called an index. The idea
behind an index is similar to the idea behind the indexes used in common
textbooks. A textbook index lists important terms at the end of the book in
alphabetical order. Along with each term, a list of page numbers where the
term appears is given. We can search the index to find a list of page numbers
(addresses) and easily locate the term in the textbook by searching the
specified pages. Hence, the index is an exact indication of where each term
occurs in the textbook.
When you define a secondary key and mark this key as active, the system will
automatically maintain an index reflecting the sorting order defined by the
key. Multiple secondary keys may be active at the same time.
A secondary key can be changed into an inactive key. This means that the
DBMS does not maintain its index. An inactive key does not take up time or
database space during table updates.
Inactive keys can be reactivated; this process may consume some time
depending on the size of the table, because the DBMS has to scan the entire
table to rebuild the index.

172

The NAVISION DBMS

The Structure of the Database

The fields comprising the secondary keys are not guaranteed to contain
unique data; the DBMS does not reject records with duplicate data in
secondary key fields. If two or more records contain identical information in
the secondary key, the DBMS will use the primary key for the table to solve
this conflict. The example below shows how the primary key influences the
sorting order when a secondary key has been activated:
We assume that the Customer table includes four entries (records). The
records in the Customer table have two fields: CustomerNo and
CustomerName. The Keylist for the Customer table is :
Key no. Key type

Definition

Primary key <CustomerNo>

Secondary

<CustomerName>

The Customer table in the primary key sorting:


CustomerNo

CustomerName

001

PC&C

002

IBM

003

Lotus

004

PC&C

When selecting the secondary key, the CustomerName field is used as basis
for the ordering. As the contents of these fields are not unique, the records
must be subsorted according to the primary key.
CustomerName CustomerNo
IBM

002

Lotus

003

PC&C

001

PC&C

004

The last two records are sorted, reflecting the values in the primary key.

Impact on NAVISIONs Working Speed


Searching for specific data is normally easier if several keys have been
defined and maintained for the table holding the desired data. The indexes for
each of the keys provide specific views which enables flexibile searches to be
performed quickly. However, there are both advantages and drawbacks in
using a large number of keys. Consider the following two situations.
Situation A: You mark a large number of secondary keys as active. This will
reduce the working speed of NAVISION, when you enter new data, as the
system will need time to maintain the indexes for each secondary key. But on
the other hand you will now be able to retrive data from the database in
several different sorting sequences very fast, because the data already is
sorted according to your definitions.
Situation B: You decide to use only a few keys. The working speed will be
increased compared to situation A, as the system now only has a few indexes

The NAVISION DBMS

173

The Structure of the Database

to maintain. But now you are not able to retrive your data as easily as before.
In order to retrieve the desired data set from the database, you will often have
to define or reactivate secondary keys in order to obtain appropriate sortings.
Depending on the size of the database, this may consume some time, as the
system builds the new index.
The decision whether to use a few or many keys are not easy to discuss in
general. The choice of appropriate keys and the number of active keys to use,
should be selected as the best compromise between the speed of data retrieval
and the speed of data updates (operations which insert, delete or modify
data). In general it may be worthwile to deactivate complex keys, if such
keys are only used on rare occations.
The overall speed of NAVISION will depend strongly upon a number of
factors:
The size of your database
The number of active keys
The complexity of the keys
The number of records in your tables.
The speed of your hardware, that is, the speed of your computer and its
disksystem.

The Key List


Figure 6 illustrates a part of the key list in the table description for the table
no. 21 Cust. Ledger Entry in the NAVISION demo application. We consider
the first four keys bound to this table - the primary key and three secondary
keys. The primary key consists of a single field ID. The first secondary key
contains two field IDs, while the second and third secondary keys
respectively contain three and four fields.
Figure 6. An
example of a key
list from a table
description.

Key Description
1 (Entry No.)

Primary key

3 (Customer No.) 4 (Date)

Secondary Key

5 (Document Type) 6 (Document No.) 3 (Customer No.)

Secondary Key

3 (Customer No.) 36 (Open) 43 (Positive) 37 (Due Date)

174

The NAVISION DBMS

Secondary Key

The Structure of the Database

Companies
The DBMS can access only one logical database at a time, but this database
can be divided into one or more companies. A company is a "sub-database",
and its primary use is to separate and group data in the same database. As
mentioned at page 170 fields and tables are identified by a number.
Companies are not identified by a number, but by a name. A company
"bundles" one or more data tables together, into a logical superstructure
which is identified by a company name. Other than the shared company
name, the different tables within a company have nothing else in common.
Opening a company is your first step after opening the database or
connecting to a server.
Figure 7 shows a database with four tables. The four table descriptions,
appearing to the left, apply to each of the data tables, which are logically
sorted into three companies. The records in the tables G/L Account,
Customer and Vendor, all have the same structure and the same field
definitions, even though they belong logically in three different companies.
Only the data stored in the fields differ.
Figure 7. This illustrates
sharing of data between
different conpanies

Company

Company

Company

G/L Account

Data

Data

Data

Customer

Data

Data

Data

Vendor

Data

Data

Data

Table Description

Printer Selection

Common Data

The idea of a company can be explained by an analogy with records in AL:


When working with records in AL you can use a WITH statement in order to
tell the system that all references you make to fields, is to fields within a
specific record. That is, within the scope of the WITH statement, you do not
explicitly need to refer to record.field but just to field. One could say that
record is assumed as default. Likewise, by opening a company you specify a
default group of tables to which all your database accesses will be directed to.
Even though you have selected a specific company, you can still access data
in any table in any other company. In order to do so, you must use the AL
function dbSELECTCOMPANY to explicitly define which other company
you want to access.
Multiple applications can access the same company and the same table(s) at
one time. How the DBMS controls these multiple accesses are described in
the section Table Locking on page 185.

The NAVISION DBMS

175

The Structure of the Database

Special Database Fields


In addition to the conventional data fields, which simply hold values, two
kinds of specialized fields are available for data manipulation:
Calculated fields
Calculation filter fields

Calculated Fields
Calculated fields is a powerful feature in the NAVISION database system.
The Calculated field is a fundamental concept which highly influences the
way a NAVISION application is designed.
Calculated fields and the underlying concept of Sum fields have been
designed in order to increase the performance in activities, such as
calculating the balance of your customers, which in traditional database
systems involves a series of accesses and calculations before a result is
available. Why such a result will be immediately available via the use of
Calculated fields will be clear as you read through the rest of this section and
Appendix A, which deals with the underlying concept of Sum fields.
Calculated fields are not stored together with the table data, why a Calculated
field can be thought of as a virtual field, which is an extension to the table
data. When a table is opened, the values in Calculated fields are set to 0
(zero). In order to update a calculated field, the programmer must use the AL
function dbCALCFIELDS or the C-Library function DBL_CalcFields().
There are two types of Calculated fields:
Yes/No. A Calculated field of this type is used to signal if any records
exists within a specified range in another table.

Decimal No. A Calculated field of this type contains the sum of a column
within a specified range in another table.

Figure 8 illustrates a Customer table in which the records contain a calculated


field of the Yes/No type named AnyEntries. The value in this Calculated field
reflects whether there exists any entries (records) for each of the customers.
The Customer table in figure 8 also contains a Decimal No. type Calculated
field named Balance. As illustrated in the figure, the Calculated field
Balance, reflects the sum of specific subsets of a column in the Customer
Entries table.
The figure shows that the value in the Calculated field Balance for customer
no. 10000 (Windy City Solutions), is retrieved from the Amount column in
the Customer Entries table. The value is the sum of the amount fields for the
entries which have the customer no. 10000, that is:
Sum = 10 + 20 + 30 = 60.
The values shown in the Balance column in the Customers table for customer
no. 10010, 10020, 10040 are found in the same way. Customer no. 10030 has
the value 0 (zero), as there are no entries in the Customer Entries table which
has a CustNo that equals 10030.
In this example each calculated field in the Customers table reflects the sum
of a specific subset of the Amount fields in the Customer Entries table. How

176

The NAVISION DBMS

The Structure of the Database

the calculation of a calculated field is to be made, are defined in a Calculation


Definition.
Figure 8. Calculated fields in the
Customer table.

Customers (Table data)


CustNo
10000

CountryCode

Name

Balance

AnyEntries

(calc. field)

(calc. field)

Windy City Solutions

US

60

Yes

10010

Modern Cars Inc.

US

90

Yes

10020

Jean Saint Laurent

FR

210

Yes

10030

Russel Publishing

UK

No

10040

La Cuisine Francais

FR

300

Yes

Customer Entries (Table data)


CustNo

Date Comment

Virtual part of
the table data

Amount

10000

10

10000

20

10000

30

10010

40

10010

50

10020

60

10020

70

10020

80

10040

90

10040

100

10040

110

Each Calculated field has an associated Calculation Definition which


includes the following information:
Table ID. Identification of the table that hold the information to be used
in the calculated field.
Filter. A list of filters to be used in the computation of the Calculated
field. The filters can be either:
- Constant expressions
- Values from ordinary fields
- Calculation Filter fields (which are discussed in the next section)
It is a requirement that there exists a key for the other table, which
includes the fields used in the filters.
The next two elements in the Calculation Definition are only used for
Calculated fields of the Decimal No. type.
Sum Field. Defines the field in the sub-table which is used as basis for
the computation of the Calculated field. It is a requirement that the field
(in the sub-table) to be summed is defined as a sum field in the key

The NAVISION DBMS

177

The Structure of the Database

definition. Sum fields are the basis for the operations and flexibility
provided by the Calculated fields. Sum fields are discussed in
appendix A.
Reverse sign. Makes it possible to shift the sign of the result in the
Calculated field.

The second special database field, the Calculation Filter field, is used in
connection with the above mentioned list of filters.

Calculation Filter Fields


End users may want to limit calculations to a subset of values in a column
which has some specific properties. For example the user may want only to
sum up the amounts of customer entries which are entered in april, or for
customers who have customer numbers greater than 10040. This is possible if
the application has been designed using Calculation Filter fields in
connection with the Calculated fields.
Figure 9 illustrates the relations between various types of database fields and
the Calculation Definition. The filters defined in the Calculation Definition
can consist of constants, values from ordinary fields and of filters given as
parameters in Calculation Filter fields. Calculation Filter fields are fields in
which the end user can input a filter (via the user interface in a NAVISION
application), which will affect the calculation of a calculated field. The
computation of a Calculated field is only affected if the Calculation Filter
field is used in the filters in the Calculation Definition..
Figure 9.The connection between
Calculation Filter fields, the Calculation
Definition and
Calculated fields

Table B

Table A

Constants

Ordinary fields

Calc. filter fields

Calculation

Table C

Definition
Calc. fields

Table D
: Path for information used in Calculation Definition
: Path for data used in computation of Calc. Fields

178

The NAVISION DBMS

Important Tasks for the DBMS

Important
Tasks for
the DBMS

In this section we discuss some important tasks for the DBMS. The topics
described in this section cover some of the most powerful features in the
NAVISION database system.
A main task for the DBMS is to protect your data from any kind of damage or
corruption. The data protection mechanisms build into the NAVISION
DBMS are illustrated in figure 10, which gives a structural overview. As the
figure suggests, the data protection mechanisms fall into two main
categories.
Mechanisms to ensure the...
1. Data integrity. We explain how the DBMS manages the integrity of your
data.
2. Data security. We present the techniques used to protect the database
against persons who are not authorized to access the database.

Figure 10. The DBMS


includes mechanisms to
ensure the data integrity
and the security.

DBMS Tasks
Data Protection:
- Data Integrity
- Data Security

Logical Database

The concrete combination of features in your NAVISION system is highly


dependent upon the configuration of the database; whether it is running
single-user or multi-user, that is, whether it is implemented locally or across a
network.
Although the concepts of data integrity and data security are quite distinct,
there are some similarities between them. Both these concepts can be stated
in terms of constraints:
Integrity is specified in terms of integrity constraints, whereas security is
specified in terms of authorization constraints.
The DBMS monitors the user interaction with the database to ensure that
both integrity and security constraints are observed.

Data Integrity
Data integrity deals with the reliability of the data stored in the database, that
is, the requirement that the database must describe the real world as credible
as possible. The NAVISION means to obtain data integrity are described in
the following sections:
Write Transactions and Recovery. We present a logical unit of

The NAVISION DBMS

179

Important Tasks for the DBMS

database processing called a write transaction, and we discuss the


NAVISION strategy for recovery from transaction failures.
Read Consistency and Concurrency. We discuss the mechanisms
which assures consistency of the data when you read from a database in a
multi-user environment.
Table Locking. We explain how the DBMS ensures the integrity of the
data in a multi-user environment.
Deadlock Detection. We explain the problem of deadlocks, and the
NAVISION strategy for solving it.
Commit. We discuss the differences between committing database
updates when using AL and C code.

Write Transactions and Recovery


We define a write transaction in NAVISION as an atomic unit of work on the
database, which is either completed in entirety or not done at all. In other
words, a transaction is a way to encapsulate a sequence of read and write
operations on the database, in order to ensure that either all or none
operations are performed on the database. The concept of write transactions
is a general NAVISION facility which are used both in single- and multi-user
environments.
When a transaction is submitted to the NAVISION DBMS the system is
responsible for making sure that:
All operations in the transaction is completed successfully and their effect
is recorded permanently in the database, or
The transaction has no effect whatsoever on the database.
The DBMS must prevent that some operations of a transaction are applied to
the database while others are not. A situation like this can occur if a
transaction fails while executing the operations of a transaction.
Some typical reasons for a transaction to fail are:
The user decides to abort the transaction.
System crash, due to hardware or software errors.
Operation errors, such as overflow or division by zero.
If the transaction is aborted all tables are restored to the state they had before
the transaction started.
A typical example of a write transaction is illustrated by a banking system
where $100 must be transferred from one account to another. This involves
two operations in a single database:
Subtract $100 from account A
Add $100 to account B
If a power failure or some other fatal error interrupts the program after the
first operation, the database is not in a consistent state, because the second
operation has not been completed. By bundling both operations into a single
transaction, either none or both of the operations are executed, and the data
will always be consistent.

180

The NAVISION DBMS

Important Tasks for the DBMS

More on Write Transactions


In the previous section you learned that by bundling a number of operations
on the database into a single transaction, we can asure that the database is
consistent no matter whether the transaction is committed or aborted. The
way NAVISION handles write transactions and at the same time keeps the
database consistent, is different from the traditional approach. Traditionally
database systems contain a facility that automatically maintains a log file
which records all changes to the database. This log file contains images of the
record before it is modified and after it is modified, so-called before and after
images. The changes recorded in the log file can be used to recover the
database from failures.
Assume that an application program aborts because of power failure, or is
aborted by the operator. The database is now in an inconsistent state, and all
modifications already made to the database must be cancelled. In common
database systems this is achieved by so-called Roll back recovery, that is, by
backing out the updates of the application program. This backout is
performed by reading the log file backwards and performing the recorded
changes to the database, until the point where the application program
started. This restores the modified records to their original contents.
The NAVISION DBMS does not need to use a log file, as the NAVISION
database is data-version oriented. This means that each time a transaction is
committed, a new version of the database is created. While you enter new
data in the database your changes are private, first when you commit the
changes, the new data becomes public and establishes the newest version.
The DBMS enables different applications to access and modify the database
concurrently by letting them work on individual versions which are
snapshots of the database at the point in time where the applications start to
access the database. The advantages of the data version approach will
become clear as you read through the following sections.

Read Consistency and Concurrency


NAVISION is data-version oriented, meaning that each time a write
transaction is performed, a new version of the data in the database is created.
Figure 11 shows three applications accessing the same database. Imagine that
the first access is made by a report. The second access is made by a user who
inserts new entries in the database, and the third access is made by a backup

The NAVISION DBMS

181

Important Tasks for the DBMS

procedure.
Figure 11. Three
application
accessing different version of the
database

Version

D
Time

(A)
Report

Entry

Backup

(B)

The generation of a report in figure 11 is a time consuming process, and


while the report is generated, another user enters or modifies records in the
database. When each entry is committed a new version of the database is
created, but as the report started, a snapshot of the database was made and the
report continues to work on version A of the database. A third user starts a
backup procedure. At the point in time where this process is started, the most
recent version of the database is B, and a snapshot of the database is made,
why the backup process is not influenced by the second user who continues
to enter new data durring the backup process. This example shows that
working with dataversions makes it possible for many users to acces the
database without interfering with each other.
The implications of the data-version approach are many; most important is
that different applications may be reading different versions of the same
database. These versions are snapshots of the database at the point in time
where the applications start to access the database. In this way the DBMS
allows for concurrency while still maintaining read consistency. If the
accesses involve only data retrieval and no changes, then the newest version
will persist - for all applications - until a write transaction is performed.
When you update the database, your modifications are private. First when
you commit your updates does your modifications become public. Your
newly committed updates plus the part of the database which was not
modified makes up the newest version.
Figure 12 illustrates the concept of data versions. The data in the
NAVISION database is stored in a well known data structure which
resembles a tree. A tree data structure is formed of nodes. Each node in the
tree, except for a special node called the root, has one parent node and one or
more child nodes. The root node has no parent. A node that has no child node
is called a leaf; a nonleaf node is called an internal node. The level of a node
is defined as one more than the level of its parent, with the level of the root
node being zero.
The data structure used in the NAVISION database is called a B+ tree. This
means that the tree structure is balanced and that the data (records) are only
stored in the leaf nodes, not in the internal nodes. A balanced tree has the
advantage that there always is a minimum number of levels in it, why all
search paths will be the shortest possible. A B+ tree structure is an efficient

182

The NAVISION DBMS

Important Tasks for the DBMS

data structure which enables fast searches to be performed.


Imagine that the tree structure in our database contains a branch with
customers A, B and C. Furthermore there are two free database blocks
available.
Assume that you need to modify customer A and C. When you update the
records, the DBMS makes a copy of the original. As illustrated in figure 12,
the copies will use two free database blocks. You will then perform your
modification to the copies, and the system will create a new internal node.
Figure 12. Illustration of free
database blocks
and data versions

Data Versions
Database
Version 1:

Free

C
Free

Database
Version 2:

C1
A1

If an error occurs during the transaction or the user decides to abort the
changes, the database blocks occupied by the copied branch will be released
and be available for new database updates.
If the transaction is committed, this new internal node will replace the old
node, and the database blocks used by the old versions of customer A and B
will now be available as free database blocks which can be used by database
updates.
The database contains a number of historical versions. Gradually, as the free
area in the database is consumed by succeeding historical versions, new
versions begin to replace the oldest versions.
Slow operations can run into trouble in this environment. Suppose
Application A is reading data from the latest version, while generating a very
time-consuming report. In the meantime, Application B begins performing
write transactions which consist of order entries.
As order entries are added to the database, newer versions of the database is
created. The maximum number of historic versions in your database, depends
on the space in the database which currently is not used by the newest

The NAVISION DBMS

183

Important Tasks for the DBMS

version, that is:


The maximum defined size of the database
- The amount of space currently used to hold the newest version
Space available for historic versions
At some point the data version accessed by A becomes the oldest complete
data version. But B needs a database block from this version so it can
complete its modifications.
This conflict is solved by the DBMS by giving priority to the write
transaction and ejecting application A. A runtime error message is sent to A
on its next read operation - "Data version is no longer valid" - and it is forced
to restart the entire process with the newest version. But as long as B
continues and the space in the database available for historic versions
remains the same, there is little hope that A will be able to generate the
report.

Table Locking
In multi-user environments the DBMS ensures the integrity of the data, by
setting write locks on all the tables you are updating. This prevents other users
from making changes to the same tables.
While write operations automatically lock a table during updates, you can
explicitly lock a table, even if you are not certain a write operation will be
performed. By locking a table immediately before accessing a record, you
are assured that the data you eventually might change in the record conforms
to the data you have read, even if some time passes in between. A write lock
does not influence data retrieval; that is, although a table is locked, it does not
prevent other users from read access to the records in the table.
A write lock is active until the write transaction is either aborted or
committed. Figure 13 uses pseudo language mnemonics to illustrate the

184

The NAVISION DBMS

Important Tasks for the DBMS

scope of write locks.


Figure 13. The
scope of write
locks

Table Locking
.
.

.
.
.

Table B locked

Table A locked

BeginWriteTransaction;
LockTable(TableA)
FindRec(TableA, ...);

(1)
(2)

InsertRec(TableA, ...)

(3)

InsertRec(TableB, ...);

(4)

.
.
.
.
.

EndWriteTransaction

(5)

.
.

The figure illustrates both an explicit lock and an automatic lock. Line (1) in
the write transaction explicitly locks table A. If this explicit lock was not set
on table A, the DBMS would automatically lock this table when a record is
inserted (3). Table B is not locked explicitly, but is locked automatically by
the DBMS when a record is inserted (4). Both locks are active until the End
Write Transaction command is executed in line (5).

Deadlock Detection
The correct functioning of a multi-user system will depend on the
coordination of the activities. If a transaction process requires write access to
several tables at once, care must be taken to avoid the situation where it can
obtain access to some of the necessary tables and another transaction process
can obtain others of them, but where neither of them can proceed without the
other finishing. This causes both of the transaction processes to wait for the
other transaction process to finish. As a result both processes will have to
wait forever. Such a situation is known as a Deadlock (or as Deadly Embrace).
In order to avoid deadlock situations, the DBMS has been provided with an
automatic deadlock detection mechanism, which detects these situations and
ejects one of the write transactions. Figure 14 illustrates how a deadlock can

The NAVISION DBMS

185

Important Tasks for the DBMS

occur.

Application 1
Time

Figure 14. The


automatic deadlock detection
ejects one of the
applications

A
A
A
A
A
A
A
A

LockTable(A)
Table AA now Locked
A
A
A
A
A
A
A
A
A
A

LockTable(B)
Wait for table B to
be unlocked
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A

Application 2
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A

LockTable(B)
Table BAA now locked
A
A
A
A
A
A
A
A
A
A
A

LockTable(A)
Wait for table A to
be unlocked
AA
AA
AA
AA
AA
AA
AA
AA

The DBMS detects a deadlock situation, and


ejects application 2.
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A

A
A
A
A
A
A
A

Application 2 ejected
and transaction aborted.
Lock attempt on Table A
cancelled and Table B
automatically unlocked

Table B locked
AA
AA
AA
AA
AA
AA
AA
AA
AA
AA
AA
AA

The DBMS will always eject the application which causes the deadlock to
occur, cf. the example shown in figure 14. This rule applies for any number
of applications involved in a deadlock.

Commit in AL and C
Although the concept of committing an update is the same whether you are
using AL code or the C-Library, there are some minor differences. This
subsection explains these differences in detail.
When you want to perform an update using the C-Library, the first thing you
must do, is to explicitly tell the system that you want to perform a write
transaction by using DBL_BWT (BeginWriteTransaction). Likewise you
must use DBL_EWT (EndWriteTransaction) to explicitly tell the system
when your write transaction ends.
When you use AL code to perform updates to a NAVISION database, these
BeginWriteTransaction and EndWriteTransaction statements are handled
implicitly by the system, that is, the system automatically executes these
commands before the AL code is entered, and when the AL code has been
executed. This means that if you only need to perform a single write

186

The NAVISION DBMS

Important Tasks for the DBMS

transaction you do not have to commit your update explicitly, it is done


automatically by the system. But if you need to perform more than one write
transaction, you have to use dbCOMMIT() in order to separate the
transactions.
Figure 15 illustrates these differences. The AL code contains two write

AL code

Figure 15. Differences in committing updates


in AL and C code

C code

BeginWriteTransaction
AL Module

AL Statements
dbCommit(...)

AL Statements
EndWriteTransaction

DBL_BWT();

}
}

1. Trans.

C code
DBL_EWT();
DBL_BWT();

2. Trans.

C code
DBL_EWT();

}
}

1. Trans.

2. Trans.

transactions. As the execution of the AL code begins, write transactions are


automatically enabled. By issuing the command dbCOMMIT(), you tell the
system that the first write transaction has ended, and prepares the system for
the second. As the execution of the AL code has been completed, the system
automatically ends the second write transaction. When using C code to
perfom the same transactions, each transaction must explicitly be
encapsulated by DBL_BWT() and DBL_EWT() commands.

Database Security and Protection


Some information may by considered as confidential or private, and cannot
be accessed legally by unauthorized persons. For example, several states in
the U.S. have privacy-of-information laws.
NAVISION provides several means of protecting data:
Access control. The NAVISION DBMS includes a password check
which prevents unauthorized persons from accessing the system itself.

Authorization control. The NAVISION DBMS provides techniques to


enable certain users in a multiuser system to access selected portions of
the database. We distinguish between direct authorization and
authorization via objects.
Data encryption. Furthermore the NAVISION database is encrypted to
protect sensitive data.

Every multi-user system demands the access protection provided by these


features.

User Access Control


In a multiuser NAVISION environment, where several users are able to
access the same database, each user must login and supply a password. The
DBMS stores a table of user IDs and passwords; these can be defined and

The NAVISION DBMS

187

Important Tasks for the DBMS

modified whenever the database is open. A user may change his password at
any time, but only a special user, the super user is authorized to create,
change and delete users, passwords and user rights.

Direct User Authorization


The NAVISION DBMS includes a database authorization subsystem which
ensures the security of portions of the database against unauthorized access.
This is done by assigning different privileges to different classes of users.
Note: If there is not at least one user designated as super user,
the system will consider all users as super users.
Users can be divided into groups which have destinct rights. For example if
there are several people who use the system to enter orders and nothing else,
the super user could define a group with the appropriate rights.
The access privileges can be defined explicitly for all Table, Function,
Window and Menu objects.
Table objects. The access privileges for table objects can be set to:
- No Access. This prevents the user from access to the table in any way.
- Read Access. This enables the user to read data from the table, but prevents the user from inserting or modifying data in the table.
- Read/Write Access. This enable the user to both read and write data.
Function, Window and Menu objects. The access privileges for these
objects can be set to:
- No Access. This prevents the user from accessing the object.
- Access. This enables the user to access the object.
The access privileges cannot be explicitly defined for other database objects
than the above. If, for example, you try to run a report object which needs
access to a window or a function object for which you do not have access
rights, an error message will occur. In other words: you cannot use objects
which needs access to other objects for which you do not have the
appropriate access rights.

Authorization via Objects


Although a users access to the database is restricted to specific areas,
extended user authorization can be granted via objects.
The super user can authorize any user access to, for example, a function
object which, in turn, allows access to parts of the database which cannot
directly be accessed by the user. The authorization to access these parts of the
databasen is defined by the developer when the function object is created, or
in other words: the extended authorization is defined inside the object.

Encryption
Security precautions continue after database manipulation stops; all data
stored to disk are automatically encrypted. The encrypted data cannot be
deciphered by low-level editors or disk utilities; only proper, authorized
accesses will yield legible material.

188

The NAVISION DBMS

Interfacing the DBMS

Interfacing
the DBMS

This section describes the NAVISION program interface. This interface


enables you to access any subset of records in a NAVISION database, in a
specific sorting order using a NAVISION application or your own C
program.
When you work on a table, a specific task will often require access to the
records in another sorting than the one defined by the primary key, that is,
your specific task requires the use of another key. Furthermore if the table
you work on contains a large number of records, you will often only need
access to a small subset of records, which have one or more properties in
common. The NAVISION means to support such data retrieval facilities are:
Table handles. The DBMS assigns a handle to the table you want to
access. A current key and one or more filters can be assigned to the table
handle.
Current key and filters. Makes it possible to change your view of the
records in a NAVISION table.
Figure 16 illustrates an application (NAVISION or C program) which
accesses a table in a NAVISION database via three handles. By using three
different handles, it is possible to get three distinct views of the records in the
table.

Figure 16. An application retrieving data


from a table using three
diffrent table handles,
each with distinct current key and filters.

Application
retrieving data
Table Handle:
Current key
Filter(s)

Handle

Handle

Handle

Database
Interface

AAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAAA
AAAAAAA
AAA

DBMS
Database
Table

...

Table

...

Table

Table Handles
When a table is closed it is identified only by a number defined by the
developer when the table was created. However, when an application
requests for access to a specific table, the DBMS assigns the table a table

The NAVISION DBMS

189

Interfacing the DBMS

handle. A handle can be thought of as a container which hold a current key


and one or more filters. This handle is returned to the application, and is now
ready to serve as a direct link to the table. Access to the table is only possible
via this handle.

When your application access a NAVISION table via a handle, all its records
are accessible. Alternatively, you can define another view of the table by
assigning a key and filters to the table handle. This allows you to open
various "views" of a single table, each with its own set of filters and a key.

Current key
As discussed in the section Secondary keys on page 173, the DBMS can
maintain up to 19 secondary keys. These secondary keys can be used when
you want to view records in an order different from the order they are sorted
according to the primary key fields. When you select a secondary key to be
used instead of the primary key, the newly activated secondary key is called
the current key.
An example: assume that the table Customer includes four entries (records).
The records in the Customer table have two fields: CustomerNo and
CustomerName. The Keylist for the Customer table is:
Key no. Key type

Definition

Primary key <CustomerNo>

Secondary

<CustomerName>

When using a handle, your default view of the records in the Customer table
will be sorted according to primary key:
CustomerNo

CustomerName

001

PC&C

002

IBM

003

Lotus

004

PC&C

Alternatively you can assign key no. 2 to the table handle, that is, define the
current key to be key no. 2. This changes your view of the Customer table
into:
CustomerName CustomerNo
IBM

002

Lotus

003

PC&C

001

PC&C

004

Filters
A filter can be set for any field in any table, to limit the number of records
upon which operations such as searches and calculations are based. When

190

The NAVISION DBMS

Interfacing the DBMS

accessing a NAVISION table via a table handle, you can assign one or more
filters to the handle, in order to suit your specific task.
You can apply a filter to one or more fields and then perform a task that
would typically affect all of the elements in the table, but now that you have
applied a filter, only those elements that fulfil the conditions of the filter are
affected. When you apply a filter to more than one field, there will implicitly
be a logical AND between these filters.
Figure 17 gives an example of the use of filters, and illustrates the effect of
the implicit logical AND between the filters. Suppose you want an overview
of the French customers in your Customer table. This can be obtained by
applying the filter FR to the Country field in the Customer table.
Furthermore you want to see which french customers have a credit limit
greater than $3000, so you will also have to apply the filter >3000 to the
Credit limit field.
Figure 17. Applying
filters on the Customer table to find
french customers
with a credit limit
greater than $3000

Filters
Filters:
Table:

>3000
Number
10000

CustomerName

10010

CreditLimit

FR
CountryCode
US

Modern Cars Inc.

1000
400

10020

Jean Saint Laurant

5000

FR

10030

Russel Publishing

34000

UK

10040

La Cuisine Francais

1500

FR

Jean Saint Laurant

5000

Windy City Solutions

US

Your View:
10020

FR

As figure 17 illustrates, you have two french customers, no. 10020 and
10040. But as customer no. 10040 has a credit limit less than $3000, your
view of the table will only show one customer, namely customer no. 10020
Jean Saint Laurant who has a credit limit of $5000.
A filter is a composite string, containing one or more of the operators shown
in the table below:
Operator Meaning
..

Range

Example

Explanation

30..40

Numbers between 30 and 40, inclusive

..310790

Dates till 310790, inclusive


The number 5 or 6

OR

5|6

&

AND

5..15 & 1..9 Numbers from 5 to 9, inclusive

<>

Different from

<> 0

Number that is not zero

>

Greater than

> 100

Number greater than 100

The NAVISION DBMS

191

Interfacing the DBMS

>=

Greater than
or equal

>= 10

Number greater than or equal 10

<

Less than

< 100

Number less than 100

<=

Less than or
equal

<= 100

Number less than or equal to 100

Contains

?Ltd.

Text containing "Ltd.".

Operator Hierarchy
The operators used in filter expressions are organized in four hierarchical
groups which determines the order in which the operations are carried out.
The lower the number of the group, the higher the precedence: The four
groups are:
1. .. (Range), () (parentheses)
2. <, >, <=, >=, <>, ?
3. & (AND)
4. | (OR)
The hierarchy causes the apparently identical expressions below to produce
different results:
Expression 1:
Expression 2:

<10 | 30..40 & >= 35, evaluates to: <10 | 35..40


(<10 | 30..40) & >= 35, evaluates to: 35..40

Figure 18 illustrates the difference between these expressions.


Figure 18. The
ranges of two
apparently identical expressions.

Effect of Operator Hierarchy


Expression 1:
10

35

40

10

35

40

Expression 2:

192

The NAVISION DBMS

Features for Increasing Performance

Features for
Increasing
Performance

This section describes special features designed to increase the run time
performance of NAVISION. We will present the following features:
The DBMS Cache
The COMMIT Cache
The Command Buffer

The DBMS Cache


The DBMS Cache is a buffer storage which holds copies of portions of the
database which currently is being used by the DBMS. As long as required
data is in cache, data appears as being immediately available. When required
data is not in the cache, required data must first be brought in from the disk.
The Cache is transparent to the user/programmer:
Data is brought into cache automatically and copied back to the physical
disk automatically.
No instruction are required to do these transfers.
The user does not have to know of the caches existence for the program
to work correctly, but the developer has to be aware of the caches
existence for the application to work well.
Figure 19 illustrates applications sending requests to the DBMS. When, for
example, Application 2 sends a request to read data from the database, the
request handler determines if the desired data can be fetched directly from
the cache, or if the data must be fetched from a disk.
Figure 19. The
DBMS Cache.

Application 1

Application 2

Application 3

Network

DBMS
DB Request Handler

Cache

Database
: Data flow

At the same time another application could be modifying a record in a table


in the database. This modification will be written to the DBMS cache, not to
the disk. When this application ends the write transaction (commit the

The NAVISION DBMS

193

Features for Increasing Performance

changes), the data in the cache modified during the transaction will be written
to the disk; the cache is said to be flushed.
The DBMS Cache always holds the most recently used data. The cache
automatically replaces parts of the cache memory with relevant parts of the
database.
The size of the cache has great impact on the performance. Two simple rules
apply when determining the size of the cache:
The more memory you assign to your cache, the more efficient the cache
will be (Of course there are no reason to assign more memory to the cache
than the total size of your database).
The size of the cache should not exceed the amount of physical memory
available on your system, as this may cause your operating system to
swap the cache memory out and in from disk; this will slow down the
overall speed of your NAVISION system tremendously.
Nevertheless, there are upper limits for the DBMS Cache size. The maximum
cache size in DOS and OS/2 versions of NAVISION is 6400 KB, while the
maximum size in UNIX versions are 32000 KB.

The Commit Cache


The Commit Cache is a special write buffer storage for the disk(s) in your
system. The Commit Cache has been designed to:
Quickly absorb committed transaction from the DBMS. This frees the
DBMS for other tasks
Enable asynchronous disk writes
Enable parallel disk writes when using multiple disks
Guarantee that the disk file always is consistent
The Commit Cache is placed between the DBMS and the database, and
absorbes committed transactions from the DBMS. When the Commit Cache
has received a committed transaction, it takes care of writing the data to the
disk(s). In this way the DBMS is free to perform other tasks while data is
being written to the disk. The data is said to be written asynchronously to the
disk, as the time for the disk write is independent of the time when the
transaction was committed by the DBMS.
As described in the section Physical vs. Logical Database on page 166, the
logical database can be stored in up to 16 distinct disk files (disks). When
using more than one disk, each of these disks are controlled by a separated
commit cache process, which are linked to each other in order to enable and
control (asynchronous) parallel write operations.
The Commit Cache always guarantees that data is written to the disk in the
same sequence as the data is sent to the Commit Cache. This assures that the
database file always is consistent. The database file is consistent, even if a
power failure should occur during a write operation to the disk.
However, the data which is currently held in the Commit Cache memory
when a power failure occurs, is lost, and must be re-entered via the

194

The NAVISION DBMS

Features for Increasing Performance

NAVISION user interface.


Note: Do not use advanced disk caches with delayed write back
(sometimes called lazy write). This may cause your database file to
be corrupted.

Figure 20 illustrates a database stored on three physical disks. Each disk is


controlled by its own commit cache process. All processes are connected to
enable parallel writing.
Figure 20. The
Commit cache
enables asynchronous parallel writing to the disks in
your system

DBMS

Commit Cache
Separate
Process

Separate
Process

Separate
Process

c:

d:

f:

Database

The Command Buffer


The Command buffer is placed as a link between your application and the
DBMS in your NAVISION system. The Command buffer is a temporary
storage place which can hold requests (AL database commands) sent from
your application to the DBMS. The Command buffer has been designed in
order to reduce the number of network transfers, when using NAVISION in
LAN environments.
When an application performs a write transaction, some requests such as for
instance inserting a record in a table (dbINSREC), does not need to be send
to the DBMS at once, but can be temporarily stored in a Command buffer. In
general, commands which are not to return a value in order for the execution
of the AL code to continue, does not need to be send immediately to the
DBMS..
Note: The contents of the command buffer is sent to the DBMS
when the buffer is full, or when a command requires an
immediate response from the DBMS.

The advantage of assembling DBMS commands into packages, is that the


number of network transfers will be reduced (that is, the load on the LAN
will be reduced), as the time required to send one DBMS request is
comparable with the time used to send an entire package.
The following illustrates an AL code sample where the command buffer

The NAVISION DBMS

195

Features for Increasing Performance

affects the number of network transfers.


WHILE dbFINDREC(Rec, -) DO
dbDELREC(Rec);

Two commands are executed for each record in the table, but each record will
only cause one request to be sent to the DBMS, as the dbDELREC command
will be stored in the Command buffer until the dbFINDREC command is
executed.
The system automatically turns off the Command buffer when you activate
the AL debugger, as unexpected results may occur if it is used when
debugging. Nevertheless, if an error occurs at run time, and you enter the AL
editor in order to find a potential bug in your code, a special situation may
occur as the Command buffer always is active when an application is
executed. Consider the following example:
(1)
(2)
(3)
(4)

dbINSREC(CustomerA);
dbINSREC(CustomerB);
dbINSREC(CustomerC);
IF dbFINDREC(CustomerX) THEN ...

If CustomerB inserted in line (2) already exists in the database, a run time
error will occur. But the error will not occur before the IF statement in line
(4) is executed, as the commands in line (1), (2) and (3) are stored in the
Command buffer until the dbFINDREC command must return a value in
order for the execution to continue.
When you enter the AL editor, the cursor will be placed on line (4), and the
following error message will be shown:
The Customer B already exists.

This might seem to be a rather strange error message for line (4), but the
explanation is simple: the error message applies to line (2), but the error was
not encountered by the DBMS until line (4) was executed by your
application.

196

The NAVISION DBMS

Appendix

Sum Fields

A sum field is a fundamental feature which forms the basis for the concept of
Calculated fields. Sum fields permits fast calculation of sums of numeric
columns in tables, even in tables with many thousands of records.
A sum field is associated to a key; each key can have at most 10 sumfields.
During database design, a field of the Decimal No. type can be associated to
a key as a sumfield. This tells the DBMS to create and maintain a structure
which contains the accumulated sum of values in a column. When a new
current key is selected, any sum fields associated with it, becomes accessible.
Figure 26 illustrates a table where the Amount field (column) is defined as a
sum field in the AccountNo + Date key. This enables the DBMS to
automatically maintain the accumulated sum of the column. Every time a
change is made to a field in the column, the accumulated values are updated.
Figure 26. A table with
sumfields, and the same
table when a calculation
filter is applied

A table sorted by AccountNo + Date:


Text

Amount

Accumulated
sum

AccountNo

Date

50000

01-01-96

100

60100

50000

01-02-96

200

90300

50020

01-04-96

300

210600

50020

01-25-96

400

1000

50040

02-01-96

500

300
1500

(sum field)

Calculation Filter:
=50020
Used in computation
for the Calculated field:
300
50020

01-04-96

300

600

50020

01-25-96

400

1000

Sum of Amount column


in specified range:

1000 - 300 = 700

To the right of the table is shown an area in the database where the

Sum Fields

197

accumulated sums for the Amount column are kept. In figure 26, the third
field in the column holding the accumulated sum, contains the value 600,
because the first three Amount values are 100, 200 and 300, respectively - a
total of 600. The fourth virtual field contains 1000, the total of the first four
values in the Amount column, and so on. If the table contained a second
sumfield, its values would be accumulated in the same way.
What advantages do sumfields offer? They allow fast calculation of sums of
columns to be performed and shown via Calculated fields. Let us say you
want the sum of all the values in the Amount fields. In a conventional
system, the DBMS is forced to access every record and add each value in the
field Amount, a very time-consuming operation in a database with thousands
of records. Here, the DBMS only needs to access the final field, where the
sum of all the Amount fields is always maintained.
Operations with sumfields are equally fast when calculation filters are
applied. The second table in figure 26 shows a group of records selected by
using a calculation filter on the AccountNo. field. Two records fulfils the
conditions of the calculation filter. Only two accesses are needed to sum
Amount for these records: one acces to get the accumulated sum associated
to the last record before the specified range, and one access to get the
accumulated sum associated to the last record in the specified range .
The value 300 is subtracted from the value 1000 to produce the correct sum
(700). No matter the number of records in the selected range, the system will
always only need to perform two accesses in order to compute the desired
sum.
The time used to maintain the accumulated sum for sum fields is negligible
due to a special index structure used in the DBMS.

The NAVISION DBMS

198

Вам также может понравиться