# Language Elements

InterSystems SQL supports the following language elements:

## Commands and Keywords

An InterSystems 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.

InterSystems 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, InterSystems SQL commands do not require or accept a semicolon command terminator. Specifying a semicolon command terminator in InterSystems SQL results in an SQLCODE -25 error. InterSystems IRIS® data platform implementation of TSQL (Transact-SQL) accepts, but does not require, a semicolon command terminator. When importing SQL code to InterSystems SQL, semicolon command terminators are stripped out.

InterSystems 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.

InterSystems 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 InterSystems SQL has no letter case restriction.

Many, but not all, keywords are SQL Reserved Words. InterSystems SQL only reserves those keywords that cannot be unambiguously parsed. SQL reserved words can be used as delimited identifiers.

## Functions: Intrinsic and Extrinsic

A function performs an operation and returns a value. Commonly in InterSystems SQL a function is specified in a SELECT statement as a select-item or in a WHERE clause, performing an operation either on a table field value or on a literal value.

Intrinsic: InterSystems SQL supports a large number of intrinsic (system-supplied) functions. These include numeric functions, string functions, and date and time functions. These functions are described in the InterSystems SQL Reference. The arithmetic and trigonometric functions are also listed in this chapter.

Aggregate functions are SQL intrinsic functions that evaluate all of the values of a column and return a single aggregate value. Aggregate functions are described separately in the InterSystems SQL Reference.

Extrinsic: InterSystems SQL can also support user-supplied ObjectScript function calls (extrinsic functions), as shown in the following example:

MySQL &sql(SELECT Name,$$MyFunc() INTO :n,:f FROM Sample.Person) WRITE "name is: ",n,! WRITE "function value is: ",f,! QUIT MyFunc() SET x="my text" QUIT x

Copy code to clipboardAn SQL statement can only invoke user-supplied (extrinsic) functions if their use is configured as a system-wide option. The default is “No”; by default, attempting to invoke user-supplied functions issues an SQLCODE -372 error. You can configure SQL use of extrinsic functions system-wide using the $SYSTEM.SQL.Util.SetOption() method, as follows: SET status=$SYSTEM.SQL.Util.SetOption("AllowExtrinsicFunctions",1,.oldval). To determine the current setting, call $SYSTEM.SQL.CurrentSettings() which displays the Allow extrinsic functions in SQL statements option.

You cannot use a user-supplied function to call a % routine (a routine with a name that begins with the % character). Attempting to do so issues an SQLCODE -373 error.

## Literals

InterSystems 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.

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

Use single quote (') characters as string delimiters. The use of the double-quote character (") is supported for SQL compatibility, but this use is strongly discouraged because of conflict with the delimited identifier standard. A pair of double quote characters "" is parsed as an invalid delimited identifier and generates an SQLCODE -1 error.

To specify a single quote character as a literal character within a string, specify a pair of these characters as the literal escape sequence. For example, 'a ''normal'' string'.

### 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).

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.

### 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 FuncCat, -- returns <null> 'fred'||NULL AS OpCat -- returns <null>

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, -- returns <null> LENGTH('') AS EmpStrLen -- returns 0

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(NULL) AS NullLen, -- returns <null> LENGTH({fn ASCII('')}) AS AsciiEmpStrLen, -- returns <null> LENGTH('') AS EmpStrLen -- returns 0

However, certain InterSystems IRIS extensions to standard SQL treat the length of NULL and the empty string differently. The $LENGTH function returns the InterSystems IRIS 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 NullLen, -- returns 0 $LENGTH('') AS EmpStrLen, -- returns 0 $LENGTH('a') AS OneCharStrLen, -- returns 1 $LENGTH(CHAR(0)) AS CharZero -- returns 0

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 NullLen, -- returns 1 CHAR_LENGTH(%STRING('')) AS EmpStrLen -- returns 2

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 ) WRITE !,"SQL empty string length: ",$LENGTH(y)

## Arithmetic Operators and Functions

InterSystems 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 InterSystems 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 InterSystems 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.

### Resulting Data Type

When performing an arithmetic operation on two numeric values having different data types, the resulting data type is determined as follows:

For addition (+), subtraction (-), integer division (\), and modulo (#):

NUMERIC | INTEGER | TINYINT | SMALLINT | BIGINT | DOUBLE | |
---|---|---|---|---|---|---|

NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | DOUBLE |

INTEGER | NUMERIC | BIGINT | BIGINT | BIGINT | BIGINT | DOUBLE |

TINYINT | NUMERIC | BIGINT | SMALLINT | INTEGER | BIGINT | DOUBLE |

SMALLINT | NUMERIC | BIGINT | INTEGER | INTEGER | BIGINT | DOUBLE |

BIGINT | NUMERIC | BIGINT | BIGINT | BIGINT | BIGINT | DOUBLE |

DOUBLE | DOUBLE | DOUBLE | DOUBLE | DOUBLE | DOUBLE | DOUBLE |

For multiplication (*) or division (/):

NUMERIC | INTEGER | TINYINT | SMALLINT | BIGINT | DOUBLE | |
---|---|---|---|---|---|---|

NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | DOUBLE |

INTEGER | NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | DOUBLE |

TINYINT | NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | DOUBLE |

SMALLINT | NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | DOUBLE |

BIGINT | NUMERIC | NUMERIC | NUMERIC | NUMERIC | NUMERIC | DOUBLE |

DOUBLE | DOUBLE | DOUBLE | DOUBLE | DOUBLE | DOUBLE | DOUBLE |

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. InterSystems SQL can be configured to support either type of precedence:

At InterSystems IRIS 2019.1 and subsequent, InterSystems SQL supports ANSI precedence of arithmetic operators by default. 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.

Default ANSI precedence is supported for a clean install of InterSystems IRIS 2019.1; when you upgrade InterSystems IRIS 2018.1 to InterSystems IRIS 2019.1, the operator precedence remains configured to the InterSystems IRIS 2018.1 default: strict left-to-right order.

At InterSystems IRIS 2018.1, InterSystems SQL does not provide precedence of arithmetic operators by default. By default, InterSystems 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.

You can configure either type of SQL operator precedence system-wide using the $SYSTEM.SQL.Util.SetOption() method, as follows: SET status=$SYSTEM.SQL.Util.SetOption("ANSIPrecedence",1,.oldval) sets ANSI precedence; SET status=$SYSTEM.SQL.Util.SetOption("ANSIPrecedence",0,.oldval) sets strict left-to-right evaluation. To determine the current setting, call $SYSTEM.SQL.CurrentSettings() which displays the Apply ANSI operator precedence option. Changing this SQL option takes effect immediately system-wide. Changing this option causes all cached queries to be purged system-wide.

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

InterSystems 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. |

InterSystems 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. |

## 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 InterSystems IRIS 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.

### Contains and Follows Operators

InterSystems SQL also supports the Contains and Follows comparison operators:

[ | Contains operator. Returns all values that contain the operand, including values equal to the operand. This operator uses EXACT (case-sensitive) collation. The inverse is NOT[. |

The Contains operator determines if a value contains a specified character or string of characters. It is case-sensitive.

The %STARTWITH predicate condition determines if a value starts with a specified character or string of characters. It is not case-sensitive.

InterSystems SQL Search can be used to determine if a value contains a specified word or phrase. SQL Search performs context-aware matching. It is not case-sensitive.

] | Follows operator. Returns all values that follow the operand in collation sequence. Excludes the operand value itself. This operator uses the field’s default collation. The InterSystems IRIS default is not case-sensitive. The inverse is NOT]. |

For example, SELECT Age FROM MyTable WHERE Age ] 88 returns 89 and greater, but also returns 9 because 9 is after 88 in the collation sequence. SELECT Age FROM MyTable WHERE Age > 88 returns 89 and greater; it does not return 9. A string operand such as ‘ABC’ collates before any string that contains additional characters, such as ‘ABCA’; therefore, to exclude the operand string from a ] operator or a > operator you must specify the entire string. Name ] ‘Smith,John’ excludes ‘Smith,John’ but not ‘Smith,John P.’

## 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

InterSystems 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.

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 syntax /*#OPTIONS */, with no space between the /* and the #, specifies a comment option. A comment option is not a comment; it specifies a code option that the query optimizer uses during the compile of the SQL query. A comment option is specified using JSON syntax, commonly a key:value pair such as the following: /*#OPTIONS {"optionName":value} */.

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 system-wide by setting the $SYSTEM.SQL.Util.SetOption() method, as follows: SET status=$SYSTEM.SQL.Util.SetOption("RetainSQL",1,.oldval). To determine the current setting, call $SYSTEM.SQL.CurrentSettings(), which displays the Retain SQL as Comments setting. The default is 1 (“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.