Previous section   Next section

Language Elements

Caché SQL supports the following language elements:

Commands and Keywords

A Caché SQL command (also known as an SQL statement) begins with a keyword followed by one or more arguments. Some of these arguments may be clauses or functions, identified by their own keywords.
  • Caché SQL commands do not have a command terminator, except in specific cases such as SQL procedure code or trigger code, in which case SQL commands are terminated by a single semicolon (;). Otherwise, Caché SQL commands do not require or accept a semicolon command terminator. Specifying a semicolon command terminator in Caché SQL results in an SQLCODE -25 error. Caché implementation of TSQL (Transact-SQL) accepts, but does not require, a semicolon command terminator. When importing SQL code to Caché SQL, semicolon command terminators are stripped out.
  • Caché SQL commands have no whitespace restrictions. If command items are separated by a space, at least one space is required. If command items are separated by a comma, no space is required. No space is required before or after arithmetic operators. You may insert line breaks or multiple spaces between space-separated items, between items in a comma-separated list of arguments, or before or after arithmetic operators.
Caché SQL keywords include command names, function names, predicate condition names, data type names, field constraints, optimization options, and special variables. They also include the AND, OR, and NOT logical operators, the NULL column value indicator, and ODBC function constructs such as {d dateval} and {fn CONCAT(str1,str2)}.
  • Keywords are not case-sensitive. By convention, keywords are represented by capital letters in this documentation, but Caché SQL has no letter case restriction.
  • Many, but not all, keywords are SQL Reserved Words. Caché SQL only reserves those keywords that cannot be unambiguously parsed. SQL reserved words can be used as delimited identifiers.

Literals

Caché SQL literals have the following syntax:
literal ::= 
     number | string-literal

number ::=  
     {digit}[.]digit{digit}[E[+|-]digit{digit}] 

digit ::=
     0..9

string-literal ::= 
     std-string-literal | ObjectScript-empty-string

std-string-literal ::= 
     ' {std-character-representation} '

std-character-representation ::=
     nonquote-character | quote-symbol

quote-symbol ::= 
     ''

ObjectScript-empty-string ::= 
     ""
A literal is a series of characters that represents an actual (literal) value. It can be either a number or a character string.
  • A number does not require any delimiter character. It can consist of the digits 0 through 9, the decimal point character, the exponent symbol and the plus and minus signs. Only one decimal point character can be used in a number. This decimal point can only be used in the base portion of a number, not in the exponent portion. The decimal point does not need to be followed by a digit. Leading and trailing zeros are permitted. The exponent (scientific notation) symbol is the letter E; both uppercase and lowercase E are accepted, but uppercase E is the preferred usage. A plus or minus sign can prefix a base number or an exponent. Multiple plus and minus signs can prefix a base number; SQL treats these signs as operators. Only a single plus or minus sign can prefix an exponent; SQL treats this sign as part of the literal. No commas or blanks are permitted in a number.
  • A character string literal consists of a pair of delimiter characters enclosing a string of characters of any type. The preferred delimiter character is the single-quote character (see below). To specify a delimiter character as a literal within a character string, double the character; for example: 'Mary''s office'.
The empty string is a literal string; it is represented by two single-quote characters (''). NULL is not a literal value; it represents the absence of any value. For further details, see the NULL and the Empty String section of this chapter.
Note:
In Embedded SQL, a few character sequences that begin with ## are not permitted within a string literal, as described in Literal Values in the “Using Embedded SQL” chapter. This restriction does not apply to other invocations of SQL, such as Dynamic SQL.

String Delimiters

You can use either single quote (') characters or the double quote (") characters as string delimiters. The single-quote (') character is the preferred delimiter. The use of the double-quote character (") is supported for SQL compatibility, but this use is discouraged because of potential conflict with the delimited identifier standard.
To specify the character used as the delimiter as a literal character within the string, specify a pair of these characters.

Concatenation

The double vertical bar (||) is the preferred SQL concatenation operator. It can be used to concatenate two numbers, two character strings, or a number and a character string.
The underscore (_) is provided as an SQL concatenation operator for ObjectScript compatibility. This concatenation operator can only be used to concatenate two character strings.
If the two operands are both character strings, and both strings have the same collation type, the resulting concatenated string has that collation type. In all other cases, the result of concatenation is of collation type EXACT.

NULL and the Empty String

Use the NULL keyword to indicate that a value is not specified. NULL is always the preferred way in SQL to indicate that a data value is unspecified or nonexistent for any reason.
The SQL zero-length string (empty string) is specified by two single quote characters. The empty string ('') is not the same thing as NULL. An empty string is a defined value, a string that contains no characters, a string of length 0. A zero-length string is represented internally by the non-display character $CHAR(0).
Note:
The SQL zero-length string is not recommended for use as a field input value or a field default value. Use NULL to represent the absence of a data value.
The SQL zero-length string should be avoided in SQL coding. However, because many SQL operations delete trailing blank spaces, a data value that contains only whitespace characters (spaces and tabs) may result in an SQL zero-length string.
Note that different SQL length functions return different values: LENGTH, CHAR_LENGTH, and DATALENGTH return SQL lengths. $LENGTH returns ObjectScript representation length. See “The Length of NULL” below. LENGTH does not count trailing blank spaces; all other length functions count trailing blank spaces.
The SQL zero-length string, like all SQL strings, can also be represented using double quote characters (""); this usage should be avoided because of potential conflict with SQL delimited identifiers.

NULL Processing

The NOT NULL data constraint requires that a field must receive a data value; specifying NULL rather than a value is not permitted. This constraint does not prevent the use of an empty string value. For further details, refer to the CREATE TABLE command.
The IS NULL predicate in the WHERE or HAVING clause of a SELECT statement selects NULL values; it does not select empty string values.
The IFNULL function evaluates a field value and returns the value specified in its second argument if the field evaluates to NULL. It does not treat an empty string value as a non-NULL value.
The COALESCE function selects the first non-NULL value from supplied data. It treats empty string values as non-NULL.
When the CONCAT function or the concatenate operator (||) concatenate a string and a NULL, the result is NULL. This is shown in the following example:
SELECT {fn CONCAT('fred',NULL)} AS FuncCatNull,
       'fred'||NULL AS OpCatNull 
The AVG, COUNT, MAX, MIN, and SUM aggregate functions ignore NULL values when performing their operations. (COUNT * counts all rows, because there cannot be a record with NULL values for all fields.) The DISTINCT keyword of the SELECT statement includes NULL in its operation; if there are NULL values for the specified field, DISTINCT returns one NULL row.
The AVG, COUNT, and MIN, aggregate functions are affected by empty string values. The MIN function considers an empty string to be the minimum value, even when there are rows that have a value of zero. The MAX and SUM aggregate functions are not affected by empty string values.

NULL in Expressions

Supplying NULL as an operand to most SQL functions returns NULL.
Any SQL arithmetic operation that has NULL as an operand returns a value of NULL. Thus, 7+NULL=NULL. This includes the binary operations addition (+), subtraction (-), multiplication (*), division (/), integer division (\), and modulo (#), and the unary sign operators plus (+) and minus (-).
An empty string specified in an arithmetic operation is treated as a value of 0 (zero). Division (/), integer division (\), or modulo (#) by empty string (6/'') results in a <DIVIDE> error.

The Length of NULL

Within SQL, the length of a NULL is undefined (it returns <null>). The length of an empty string, however, is defined as length zero. This is shown in the following example:
SELECT LENGTH(NULL) AS NullLen, 
       LENGTH('') AS EmpStrLen 
As shown in this example, the SQL LENGTH function returns the SQL lengths.
You can convert an SQL zero-length string to a NULL by using the ASCII function, as shown in the following example:
SELECT LENGTH('') AS EmpStrLen,
       LENGTH({fn ASCII('')}) As NullLen
However, certain Caché extensions to standard SQL treat the length of NULL and the empty string differently. The $LENGTH function returns the Caché internal representation of these values: NULL is represented as a defined value with length 0, the SQL empty string is represented as a string of length 0. This functionality is compatible with ObjectScript.
SELECT $LENGTH(NULL) AS NullLenZero, 
$LENGTH('') AS EmpStrLenZero, 
$LENGTH('a') AS StrLenOne, 
$LENGTH(CHAR(0)) AS CharLenZero  
Another place where the internal representation of these values is significant is in the %STRING, %SQLSTRING and %SQLUPPER functions, which append a blank space to a value. Since a NULL truly has no value, appending a blank to it creates a string of length 1. But an empty string does have a character value, so appending a blank to it creates a string of length 2. This is shown in the following example:
SELECT CHAR_LENGTH(%STRING(NULL)) AS BlankLenOne,
CHAR_LENGTH(%STRING('')) AS EmpStrAndBlankLenTwo
Note that this example uses CHAR_LENGTH, not LENGTH. Because the LENGTH function removes trailing blanks, LENGTH(%STRING(NULL)) returns a length of 0; LENGTH(%STRING('')) returns a length of 2, because %STRING appends a leading blank, not a trailing blank.

ObjectScript and SQL

When an SQL NULL is output to ObjectScript, it is represented by an ObjectScript empty string (""), a string of length zero.
When an SQL zero-length string data is output to ObjectScript, it is represented by a string containing $CHAR(0), a string of length 1.
   &sql(SELECT NULL,''
        INTO :a,:b)
  WRITE !,"NULL length: ",$LENGTH(a)         // returns 0
  WRITE !,"empty string length: ",$LENGTH(b) // returns 1
In ObjectScript, the absence of a value is usually indicated by an empty string (""). When this value is passed into embedded SQL, it is treated as a NULL value, as shown in the following example:
  SET x=""
  SET myquery="SELECT NULL As NoVal,:x As EmpStr"
  SET tStatement=##class(%SQL.Statement).%New()
  SET qStatus=tStatement.%Prepare(myquery)
  IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET rset=tStatement.%Execute()
  WHILE rset.%Next() {
    WRITE "NoVal:",rset.%Get("NoVal")," length ",$LENGTH(rset.%Get("NoVal")),!      // length 0
    WRITE "EmpStr:",rset.%Get("EmpStr")," length ",$LENGTH(rset.%Get("EmpStr")),!   // length 0
  }
  WRITE "End of data"
If you specify an input host variable that is not defined, embedded SQL treats its value as NULL.
When passing a NULL or empty string value out from embedded SQL to ObjectScript, the NULL is translated to a string of length 0, and the empty string is translated to a string of length 1. This is shown in the following example:
   &sql(SELECT 
        NULL,
        ''
        INTO :a,:b)
  WRITE !,"The length of NULL is: ",$LENGTH(a)         // length 0
  WRITE !,"The length of empty string is: ",$LENGTH(b) // length 1
In the following example, the SQL empty string with an appended blank is passed out as string of length 2:
   &sql(SELECT %SQLUPPER('')
        INTO :y
        FROM Sample.Person)
  WRITE !,"SQL empty string length: ",$LENGTH(y)

Arithmetic Operators and Functions

Caché SQL supports the following arithmetic operators:
+ Addition operator. For example, 17+7 equals 24.
Subtraction operator. For example, 17-7 equals 10. Note that a pair of these characters is the Caché SQL comment indicator. Therefore, to specify two or more subtraction operators or negative signs you must use either spaces or parentheses. For example, 17- -7 or 17-(-7) equals 24.
* Multiplication operator. For example, 17*7 equals 119.
/ Division operator. For example, 17/7 equals 2.428571428571428571.
\ Integer division operator. For example, 17\7 equals 2.
# Modulo operator. For example, 17 # 7 equals 3. Note that because the # character is also a valid identifier character, to use it as a modulo operator you should specify it separated from its operands by spaces before and after.
E Exponentiation (scientific notation) operator. Can be uppercase or lowercase. For example, 7E3 equals 7000. A too-large exponent results in an SQLCODE -7 “Exponent out of range” error. For example, 1E309 or 7E308.
() Grouping operators. Used to nest arithmetic operations. Unless parentheses are used, the execution sequence of arithmetic operations in Caché SQL is strict left-to-right order. For example, 17+7*2 equals 48, but 17+(7*2) equals 31.
|| Concatenate operator. For example, 17||7 equals 177.
Arithmetic operations are performed on numbers in their canonical form.
For numbers of any data type, the data type for the result of an addition (+), subtraction (-), multiplication (*), or division (/, \, or #) operation is NUMERIC, unless one or both of the arguments is of data type DOUBLE; in that case, the data type of the result is DOUBLE. For example, adding two fields of data type INTEGER results in a value of data type NUMERIC. Concatenating two numbers of any data type results in a VARCHAR string.
In Dynamic SQL you can use SQL column metadata to determine the data type of a result set field. For further details on numeric data types refer to SQL Data Types.

Operator Precedence

The SQL-92 standard is imprecise with regard to operator precedence; assumptions on this matter differ amongst SQL implementations.
  • Caché SQL, by default, does not provide precedence of arithmetic operators. By default, Caché SQL executes arithmetic expressions in strict left-to-right order, with no operator precedence. This is the same convention used in ObjectScript. Thus, 3+3*5 equals 30. You can use parentheses to enforce the desired precedence. Thus, 3+(3*5) equals 18. Careful developers should use parentheses to explicitly state their intentions.
  • Caché SQL can be configured to support ANSI precedence of arithmetic operators. This is a system-wide configuration setting. When ANSI precedence is configured, the "*", "\", "/", and "#" operators have a higher precedence than the "+", "-", and "||" operators. Operators with a higher precedence are executed before operators with a lower precedence. Thus, 3+3*5 equals 18. You can use parentheses to override precedence when desired. Thus, (3+3)*5 equals 30.
    You can configure precedence using the Management Portal. In the General SQL Settings, you select the Apply ANSI Operator Precedence check box, then press the Save button. Changing this SQL option takes effect immediately system-wide. Changing this option causes all cached queries to be purged system-wide.
    You can also configure precedence using the $SYSTEM.SQL.SetANSIPrecedence() method.
    Changing SQL precedence has no effect on ObjectScript. ObjectScript always follows strict left-to-right execution of arithmetic operators.

Precision and Scale

The precision (maximum number of digits present in the number) for a NUMERIC result for:
  • addition or subtraction is determined using the following algorithm: resultprecision=max(scale1, scale2) + max(precision1–scale1, precision2–scale2)+1. If the calculated resultprecision is greater than 36, the precision value is set to 36.
  • multiplication is determined using the following algorithm: resultprecision=min(36, precision1+precision2+1).
  • division (value1 / value2) is determined using the following algorithm: resultprecision=min(36, precision1–scale1+scale2+max(6, scale1+precision2+1)).
The scale (maximum number of fractional digits) for a NUMERIC result for:
  • addition or subtraction is determined using the following algorithm: resultscale=max(scale1, scale2).
  • multiplication is determined using the following algorithm: resultscale=min(17, scale1+scale2).
  • division (value1 / value2) is determined using the following algorithm: resultscale=min(17, max(6, scale1+precision2+1)).
For further details on data types, precision, and scale, refer to SQL Data Types.

Arithmetic and Trigonometric Functions

Caché SQL supports the following arithmetic functions:
ABS Returns the absolute value of a numeric expression.
CEILING Returns the smallest integer greater than or equal to a numeric expression.
EXP Returns the log exponential (base e) value of a numeric expression.
FLOOR Returns the largest integer less than or equal to a numeric expression.
GREATEST Returns the largest number from a comma-separated list of numbers.
ISNUMERIC Returns a boolean code specifying whether an expression is a valid number.
LEAST Returns the smallest number from a comma-separated list of numbers.
LOG Returns the natural log (base e) value of a numeric expression.
LOG10 Returns the base–10 log value of a numeric expression.
MOD Returns the modulus value (remainder) of a division operation. Same as the # operator.
PI Returns the numeric constant pi.
POWER Returns the value of a numeric expression raised to a specified power.
ROUND Returns a numeric expression rounded (or truncated) to a specified number of digits.
SIGN Returns a numeric code specifying whether a numeric expression evaluates to positive, zero, or negative.
SQRT Returns the square root of a numeric expression.
SQUARE Returns the square of a numeric expression.
TRUNCATE Returns a numeric expression truncated to a specified number of digits.
Caché SQL supports the following trigonometric functions.
ACOS Returns the arc-cosine of a numeric expression.
ASIN Returns the arc-sine of a numeric expression.
ATAN Returns the arc-tangent of a numeric expression.
COS Returns the cosine of a numeric expression.
COT Returns the cotangent of a numeric expression.
SIN Returns the sine of a numeric expression.
TAN Returns the tangent of a numeric expression.
DEGREES Converts radians to degrees.
RADIANS Converts degrees to radians.

Relational Operators

A conditional expression evaluates to a boolean value. A conditional expression can use the following relational operators:
= Equals operator.
!=
<>
Does not equal operator. The two syntactical forms are functionally identical.
< Less than operator.
> Greater than operator.
<= Less than or equal to operator.
>= Greater than or equal to operator.
When comparing a table field value, these equality operators use the field’s default collation. The Caché default is not case-sensitive. When comparing two literals, the comparison is case-sensitive.
Equality operators (equals, does not equal) should be avoided when comparing floating point numbers. Floating point numbers (data types classes %Library.Decimal and %Library.Double) are stored as binary values, not as fixed-precision numbers. During conversion, rounding operations may result in two floating point numbers that are intended to represent the same number not being precisely equal. Use less-than / greater-than tests to determine if two floating point numbers are “the same” to the desired degree of precision.
Caché SQL also supports the Contains and Follows comparison operators:
[ Contains operator. Returns all values that contain the operand, including values equal to the operator. This operator uses EXACT (case-sensitive) collation. The inverse is NOT[.
] Follows operator. Returns all values that follow the operator in collation sequence. Excludes the operand value itself. This operator uses the field’s default collation. The Caché default is not case-sensitive. The inverse is NOT].

Logical Operators

SQL logical operators are used in condition expressions that are evaluated as being True or False. These conditional expressions are used in the SELECT statement WHERE and HAVING clauses, in the CASE statement WHEN clauses, in the JOIN statement ON clause, and the CREATE TRIGGER statement WHEN clause.

NOT Unary Operator

You can use the NOT unary logical operator to specify the logical inverse of a condition, as shown in the following examples:
SELECT Name,Age FROM Sample.Person
WHERE NOT Age>21
ORDER BY Age
SELECT Name,Age FROM Sample.Person
WHERE NOT Name %STARTSWITH('A')
ORDER BY Name
You can place the NOT operator before the condition (as shown above). Or you can place NOT immediately before a single-character operator; for example, NOT<, NOT[, and so forth. Note that there must be no space between NOT and the single-character operator it inverts.

AND and OR Operators

You can use the AND and OR logical operators between two operands in a series of two or more conditions. These logical operators can be specified by keyword or symbol:
AND &
OR !
Spaces are not required (though recommended for readability) between a symbol operator and its operand. Spaces are required before and after a keyword operator.
These logical operators can be used with the NOT unary logical operator, such as the following: WHERE Age<65 & NOT Age=21.
The following two examples use logical operators to schedule an assessment based on age. People between the ages of 20 and 40 are assessed every three years, people from 40 to 64 are assessed every two years, and those 65 and over are assessed every year. The examples give identical results; the first example uses keywords, the second uses symbols:
SELECT Name,Age FROM Sample.Person
WHERE Age>20
      AND Age<40 AND (Age # 3)=0 
      OR Age>=40 AND (Age # 2)=0 
      OR Age>=65
ORDER BY Age
SELECT Name,Age FROM Sample.Person
WHERE Age>20
      & Age<40 & (Age # 3)=0 
      ! Age>=40 & (Age # 2)=0 
      ! Age>=65
ORDER BY Age
Logical operators can be grouped using parentheses. This establishes a grouping level; evaluation proceeds from the lowest grouping level to the highest. In the first of the following examples, the AND condition is applied only to the second OR condition. It returns persons of any age from MA, and persons with age less than 25 from NY:
SELECT Name,Age,Home_State FROM Sample.Person
WHERE Home_State='MA' OR Home_State='NY' AND Age < 25
ORDER BY Age
Using parentheses to group conditions gives a different result. The following example returns persons from MA or NY whose age is less than 25:
SELECT Name,Age,Home_State FROM Sample.Person
WHERE (Home_State='MA' OR Home_State='NY') AND Age < 25
ORDER BY Age
  • SQL execution uses short-circuit logic. If a condition fails, the remaining AND conditions will not be tested. If a condition succeeds, the remaining OR conditions will not be tested.
  • However, because SQL optimizes WHERE clause execution, the order of execution of multiple conditions (at the same grouping level) cannot be predicted and should not be relied upon.

Comments

Caché SQL supports both single-line comments and multi-line comments. Comment text can contain any characters or strings, except, of course, the character(s) that indicate the end of the comment.
Note:
Using Embedded SQL marker syntax (&sql<marker>(...)<reversemarker>) imposes a restriction on the contents of SQL comments. If you are using marker syntax, the comments within the SQL code may not contain the character sequence “)<reversemarker>”. For further details, refer to The &sql Directive in the “Using Embedded SQL” chapter of this manual.
You can use the preparse() method to return an SQL DML statement stripped of comments. The preparse() method also replaces each query argument with a ? character and returns a %List structure of these arguments. The preparse() method in the following example returns a parsed version of the query, stripped of single-line and multi-line comments and whitespace:
  SET myq=4
  SET myq(1)="SELECT TOP ? Name /* first name */, Age "
  SET myq(2)="   FROM Sample.MyTable -- this is the FROM clause"
  SET myq(3)="   WHERE  /* various conditions "
  SET myq(4)="apply */ Name='Fred' AND Age > 21 -- end of query"
  DO ##class(%SQL.Statement).preparse(.myq,.stripped,.args)
  WRITE stripped,!
  WRITE $LISTTOSTRING(args)

Single Line Comments

A single-line comment is specified by a two-hyphen prefix. A comment can be on a separate line, or can appear on the same line as SQL code. When a comment follows SQL code on the same line, at least one blank space must separate the code from the double-hyphen comment operator. A comment can contain any characters, including hyphens, asterisks, and slashes. The comment continues to the end of the line.
The following example contains multiple single-line comments:
-- This is a simple SQL query
-- containing -- (double hyphen) comments
SELECT TOP 10 Name,Age, -- Two columns selected
Home_State -- A third column
FROM Sample.Person -- Table name
-- Other clauses follow
WHERE Age > 20 AND -- Comment within a clause
Age < 40
ORDER BY Age,  -- Comment within a clause
Home_State
-- End of query

Multiple Line Comments

A multiple-line comment is specified by a /* opening delimiter and a */ closing delimiter. A comment can appear on one or more separate lines, or can begin or end on the same line as SQL code. A comment delimiter should be separated from SQL code by at least one blank space. A comment can contain any characters, including hyphens, asterisks and slashes, with the obvious exception of the */ character pair.
The following example contains several multiple-line comments:
/* This is 
   a simple 
   SQL query. */
SELECT TOP 10 Name,Age /* Two fields selected */
FROM Sample.Person  /* Other clauses 
could appear here */ ORDER BY Age
/* End of query */
When commenting out Embedded SQL code, always begin the comment before the &sql directive or within the parentheses. The following example correctly comments out two the Embedded SQL code blocks:
   SET a="default name",b="default age"
   WRITE "(not) Invoking Embedded SQL",!
   /*&sql(SELECT Name INTO :a FROM Sample.Person) */
   WRITE "The name is ",a,!
   WRITE "Invoking Embedded SQL (as a no-op)",!
   &sql(/* SELECT Age INTO :b FROM Sample.Person */)
   WRITE "The age is ",b

SQL Code Retained as Comments

Embedded SQL statements can be retained as comments in the .INT code version of routines. This is done by setting a configuration option as follows:
  • Invoke the $SYSTEM.SQL.SetRetainSQL() method. To determine the current setting, call $SYSTEM.SQL.CurrentSettings(), which displays the Retain SQL as Comments setting.
  • Go to the Management Portal. From System Administration, select Configuration, then SQL and Object Settings, then General SQL Settings (System, Configuration, General SQL Settings). On this screen you can view the current setting of Retains SQL Statements as Comments in .INT Code. The default is “Yes”.
Set this option to “Yes” to retain SQL statements as comments in the .INT code version of a routine. Setting this option to “Yes” also lists all non-% variables used by the SQL statements in the comment text. These listed variables should also be listed in the ObjectScript procedure’s PUBLIC variable list and re-initialized using the NEW command. For further details, refer to Host Variables in the “Embedded SQL” chapter of this manual.
Previous section   Next section