ECSql Operators

Bitwise operator

Operator Description Example
& Bitwise AND (4&2 ) output 0
~ Bitwise NOT urinary ( ~1 ) output -2 or 0xfffffffffffffffe
| Bitwise OR (3|4 ) output 7
<< Bitwise shift left (1<<2) output 4
>> Bitwise shift right (4>>1) output 2

Arithmetic operator

Operator Description Example
+ Add (1 + 1) output 2
- Subtract (2 - 1) output 1
* Multiply (2 * 2) output 4
/ Divide (4 / 2) output 2
% Modulo (4 % 2) output 0

String operator

Operator Description Example
|| Concatenate 'Hello'|| ',' || 'World' output Hello,World

Boolean operator

Operator Description Example
= Equal (1 = 3) output FALSE
> Greater than (1 > 3) output FALSE
< Less than (1 < 3) output TRUE
>= Greater or equal to (3 >= 3) output TRUE
<= Less or equal to (3 <= 5) output TRUE
<> Not equal (1 <> 3) output TRUE
!= Not equal (1 != 3) output TRUE
IS Null-safe equal (NULL IS NULL) output TRUE
IS NOT Null-safe not equal (1 IS NOT NULL) output TRUE
OR OR op (1=2 OR 1=1) output TRUE
AND AND op (1=1 AND 1=1) output TRUE
NOT NOT unary op NOT (1=1) output FALSE

IS / IS NOT operator (null-safe comparison)

The IS and IS NOT operators compare two operands using null-safe semantics, mapping to SQLite's IS / IS NOT operators. Unlike = and <>, a NULL operand never makes the result unknown:

  • NULL IS NULL is TRUE (whereas NULL = NULL is unknown, so the row is filtered out).
  • <value> IS NULL is FALSE when <value> is not NULL.

Each operand may be any value expression — a property, the NULL literal, a constant, a parameter, a function call, an arithmetic expression, and so on — and the NULL literal may appear on either side.

-- Rows where CodeValue and UserLabel differ, treating NULL as a comparable value SELECT * FROM [bis].[Element] WHERE [CodeValue] IS NOT [UserLabel] -- Equivalent to "CodeValue IS NULL" SELECT * FROM [bis].[Element] WHERE NULL IS [CodeValue] -- The right-hand side can be any value expression, e.g. a function call SELECT * FROM [bis].[Element] WHERE [CodeValue] IS json_extract([JsonProperties], '$.code')

For multi-column operands such as Point2d/Point3d and navigation properties, the comparison is expanded column-wise (consistent with = and <>): IS joins the per-column comparisons with AND, while IS NOT joins them with OR.

-- TRUE only when Origin and BBoxLow match on every coordinate (X, Y and Z) SELECT * FROM [bis].[GeometricElement3d] WHERE [Origin] IS [BBoxLow] -- TRUE when the two navigation properties differ on either the related Id or the relationship class SELECT * FROM [ts].[Child] WHERE [ParentA] IS NOT [ParentB]

Both operands must be type-compatible, following the same rules as = and <>: comparable primitive types (for example two strings, or numeric types compared with each other) or composite types of the same shape (Point2d with Point2d, a navigation property with a navigation property), with the NULL literal allowed against any type. Comparing unrelated types — for example a string against a Point3d — is rejected when the statement is prepared.

Note: IS [NOT] is also used by the unrelated ECClass filter predicate (<classId> IS [NOT] (<class-name>, ...)) and by the boolean truth tests IS [NOT] TRUE/FALSE/UNKNOWN. These keep their original meaning and take precedence only when the right-hand operand matches their shape: a bare NULL/TRUE/FALSE/UNKNOWN, or a parenthesized qualified class name — optionally with an ONLY/ALL prefix or written as a comma-separated list (for example (bis.Element), (ONLY bis.Element), or (bis.Element, bis.Model)). Any other parenthesized right-hand operand is a value expression: a parenthesized unqualified name such as (MyProperty) is read as that property (ECSQL class names must always be schema-qualified, so there is no ambiguity here), so prop1 IS (prop2) and x IS (y + 1) are null-safe value comparisons. A parenthesized qualified name that does not resolve to a known ECClass — for example (ts.Status.Active) — is likewise read as a value expression, so Status IS (ts.Status.Active) is a null-safe comparison; if the qualified name does resolve to a class, the type-predicate reading takes precedence, e.g. ECClassId IS (bis.Element) stays a type predicate.

ECSql Syntax

Last Updated: 03 September, 2026