The SQR language has several families of built-in functions. They range from minor conveniences to potential life savers. There are some surprising inclusions and surprising omissions. I discuss some of the functions I wish we had in An SQR Wish List.

### Math Functions

The **round** and **trunc** (truncate) functions give us control over fractions. Both of them keep the integer portion of a number and some of the decimal places. Both of them replace the least significant decimal places with zero. **Round** might increment the nth decimal place, depending on the (n+1)th decimal place; **trunc** never does. Unfortunately, we cannot round to a negative number of decimal places; -2 won’t round to the nearest hundred.

**Floor** is a special case of **trunc** where the number of decimal places is zero. It returns an integer, although it supports polymorphism (object oriented SQR?) by returning the same numeric type as its parameter. (As do **round** and **trunc**.)

**Ceil** (ceiling) is the opposite of **floor**, returning the minimum integer greater than the parameter, rather than the maximum integer less than the parameter. It’s useful with negative numbers or in touchy situations, e.g. “round an employee’s pay (in pennies) to the penny, but never downward.” There isn’t a general version of **ceil**, like **trunc**, for any number of decimal places, but we could write:

`let #general_ceil = -trunc(-#amount, #decimal_places)`

If we need the fraction rather than the integer, the answer is the **mod** function or the modulus operator (**%**).

`let #fraction = mod(#amount, 1)`

**Mod** can control cyclical functions. If we’re reading thousands of rows and want to supply periodic status reports, we can write this.

`begin-select`

…

add 1 to #num_rows

if mod(#num_rows, 1000) = 1

show #num_rows edit 999,999 ' rows read so far'

end-if

…

end-select

I learned modular arithmetic as applied to integers, but SQR is more flexible. We can even use a fractional or negative modulus. mod(9, 2.5) is 1.5. mod(9, -2.5) is also 1.5 because 9 = -2.5 * -3 (an integer) + 1.5.

**Abs** (absolute value) ensures we have a positive number, but it does not leave a trace of what it did or didn’t do. When I was exporting payroll data to general ledger, I found it better to write:

`if #amount < 0`

let $type = 'CR'

let #amount = -#amount

else

let $type = 'DR'

end-if

If I were working in C or Java and wanted to branch on whether a number was positive, negative, or zero, I would use **sign** (or switch to Fortran II).

`sign_amt = sign(amt);`

switch (sign_amt)

{

case -1:

…

case 0:

…

case 1

…

};

But SQR **evaluate** is more powerful than **switch**, eliminating the need for **sign** in this situation.

`evaluate #amt`

when < 0

…

when = 0

…

when > 0

…

end-evaluate

After this, the SQR math functions get more advanced, with square roots, trigonometry, hyperbolic trigonometry, logarithms, and exponents. I’ve tried, and failed, to imagine the customer who requested these features in a database reporting language. I suspect that once the hard compiler programming was done to parse functions, evaluate parameters, manage the stack, and call the C standard library, it was a breeze to add these functions whether they were needed or not.

On the other hand, SQR does not contain financial and business functions like internal rate of return, yield to maturity, loan payments, present value, future value, depreciation, economic order quantity, Black-Scholes option valuation, etc.

### Date Functions

I discussed date variables and date functions in the SQR Dates article. Dates are stored as strings (this article was published on ‘20091011’) and processed like strings in the **datetostr**, **strtodate**, and **edit** functions. On the other hand, they are processed mathematically with complex formulae in the **dateadd**, **datediff**, and **edit** functions.

### String Functions

I use **instr**, **substr**, **length**, and **edit** most frequently. SQR has several variations of the first three to handle multibyte characters. My second tier tools are the **lower**, **upper**, **ltrim**, **rtrim**, **lpad**, and **rpad**. I nominate **roman** for the “hyperbolic arctangent” award for coolest, least expected function. Sure, people still use Roman numerals for clockfaces, movie copyrights, and table of contents pages, but do we need a dedicated function in addition to the Roman numeral **edit** mask?

### Logic Functions

I use these functions to streamline my program flow. **Cond** and **nvl** replace if-else-endif blocks. **Range** performs two comparisons at once.

One use for **cond** is to build strings that contain lists, e.g. a dynamic variable for a clause like “and ERNCD in (‘REG’,’VAC’,’SIC’)”. Here are the rules.

- If there are no earning codes, the dynamic variable should be a null string, otherwise:
- Begin the string with “and ERNCD in (“.
- Starting with the second earning code, put a comma before the earning code.
- Put each earning code in quotes.
- End the string with “)”.

Here’s the code with **cond**.

`let $clause = ''`

begin-select

ERNCD

let $clause = $clause || cond($clause = '', 'and ERNCD in (‘, ',') || '''' || &ERNCD || ''''

…

end-select

let $clause = $clause || cond($clause = '', '', ')')

Null values are no-nos for Peoplesoft character fields, so we can use **nvl** to replace them with spaces before inserting or updating rows. If we have fifty columns to write, it’s easier to program this:

`let $address1 = nvl($address1, ' ')`

let $address2 = nvl($address2, ' ')

than this:

`if isnull($address1)`

move ' ' to $address1

end-if

if isnull($address1)

move ' ' to $address1

end-if

One use for **range** is to deal with fuzzy equalities. After a string of calculations, two floating point values might be very close, but the vagaries of binary representation might make them not quite equal. The answer is to test them like this.

`if range(#a - #b, -0.001, 0.001)`

! they are “equal”

end-if

Or like this.

`if abs(#a - #b) < 0.001`

! they are “equal”

end-if

I don’t know about SQR, but in C, floor is different from trunc (or C’s equivalent of trunc). floor(-1.5) returns -2, whereas trunc(-1.5) returns -1.

It’s the same in SQR. My description of floor was correct and my description of trunc was correct, but my statement that floor was a special case of trunc was wrong for negative numbers. Thanks for the correction.

I have a Problem understanding an existing SQR-Code, I would be very glad if you could help me:

Where is the difference between:

If a=a

Do Print-Failure(1)

ELSE

Do Print-Failure(2)

END-IF

…and an IF with a hashmark, like the following:

#IF a=a

Do Print-Failure(1)

#ELSE

Do Print-Failure(2)

#END-IF

…its only a small example of me problem.

Please tell me whats the difference and in which cases you use a hashmark…

Please make me happy 😉

Great question! Both examples will PERFORM the same way, executing the “Do Print-Failure(1)” command, but they get to that point quite differently. Running an SQR program is usually a two step process. First, the SQR interpreter (SQRW.EXE or PSSQR.EXE) reads the program and translates it into a form that is easier and faster to execute. Second, the SQR interpreter executes that form.

Your second example, with the hash marks, indicate that the SQR interpreter should not translate all your code. The translator evaluates the “a=a” expression and determines that it is true. It translates the “Do Print-Failure(1)” command and ignores the “Do Print-Failure(2)” command. The translation will be the same as if you had just coded “Do Print-Failure(1).”

Your first example translates all five lines of code. When we get to the second stage of executing code, the SQR interpreter will evaluate the IF statement every time you pass through this code, perform the first branch, “Do Print-Failure(1)”, and skip over the rest of the translated code (“Do Print-Failure(2)”).

In the first example, the translation takes a little longer because it translates five lines instead of one, and the execution takes a little longer because it has to test the expression “a=a” and branch appropriately.

The “#IF” command is a directive to the code translator. The “IF” command is a directive to the code executor. As for usage:

– #IF can only evaluate simple expressions; complex expressions require IF.

– #IF is good if you have to choose between two blocks of code and SQR won’t let you compile both. For example, two different BEGIN-SELECT blocks that use the same aliases.

– #IF is good if you have a piece of code that is not a full, stand-alone command, and you might or might not want to use it. For example: (1) part of a WHERE clause, (2) fields in a CREATE-ARRAY command.