Skip to content

Syntax & grammar

MeasureName := <expression>
// names with spaces use the bracket form
[Average Order Value] := Divide([Revenue], [Orders])

Names are unique per model (case-insensitively), up to 160 characters. Expressions are stored in a canonical form — consistent casing and whitespace — so format-on-save never changes meaning.

Form Meaning
[Measure Name] another measure in the same model
Table.Column a column, resolved against model tables (plain dotted path — no quoting)
Table.Column:grain a date column at an explicit grain: day, week, month, quarter, year
"text" or 'text' string literal (canonical form prints double quotes)
42, 4.2 numbers
true, false booleans

The inline grain is a SquareX exclusive — Fixed(Date.Date:month, …) replaces a whole class of date-truncation boilerplate. There is no date literal type: compare an ISO string against a date column (Date.Date >= "2026-01-01") and the compiler emits a properly typed date comparison on every database.

From loosest to tightest binding:

Level Operators
1 or
2 and
3 = <> < <= > >=
4 & (string concatenation)
5 + -
6 * / %
7 unary -, not

Worth knowing:

  • Logic uses wordsand, or, not — not &&/||.
  • & binds looser than arithmetic, so "total: " & ToText(a + b) needs no extra parentheses. & takes strings only — wrap numbers in ToText.
  • Unary not binds tightest: not a = b parses as (not a) = b. Write not (a = b) when that’s what you mean.
  • / is always real division (integers are cast — no silent integer division). Prefer Divide for ratios; it’s safe on zero.
  • Parentheses are always allowed.

Function names and keywords are case-insensitivesum, Sum, and SUM all work and are stored canonically. // starts a comment that runs to end of line, is legal inside expressions, and survives save — measures can document themselves:

// The override idiom: remove-then-add wins over any dashboard Region filter
WestRevenue := Calculate([Revenue], Sales.Region = "West", RemoveFilters(Sales.Region))
  • Identifiers are letters, digits, and underscores, starting with a letter or underscore; anything else in a measure name is reachable via [brackets] (a literal ] doubles: ]]).
  • Function names and keywords are reserved as bare identifiers — a measure may still be named Sum, but you’ll reference it as [Sum] (and get a SQXW02 warning suggesting a rename).
  • A Let binding can’t shadow a function name (SQX013).

Saved expressions keep parsing forever: grammar changes are additive only (new functions, new optional arguments) — existing syntax is never repurposed.