SquareX: the measure language
SquareX is DataSquares’ measure language. You define a measure once, at the data-model level, and it computes consistently everywhere the model is used — charts, dashboards, reports, exports, and the API. If you know Power BI’s DAX or Tableau’s LOD expressions, SquareX gives you the same class of power with a deliberately smaller, plainer surface: about 25 functions, no row context, and everything compiles to SQL that runs in your own database.
Your first measures
Section titled “Your first measures”A measure is a name, :=, and an expression:
Revenue := Sum(Sales.Amount)
Orders := CountDistinct(Sales.OrderId)
AvgOrderValue := Divide([Revenue], [Orders])Three things to notice:
- Columns are plain dotted paths:
Sales.Amount. No quotes, no brackets. - Measures reference each other in square brackets:
[Revenue]. Build small measures and compose them. Divideis safe by default — division by zero gives blank (NULL), not an error or infinity. The common intent is the default, not a gotcha.
Once saved, measures appear under ƒx Measures in the field tree — drag one onto a chart like any field, and it aggregates correctly at whatever grain the chart uses.
Filter context, and changing it with Calculate
Section titled “Filter context, and changing it with Calculate”A measure always evaluates inside the current filter context — whatever dashboard filters, chart filters, and grouping apply where it’s used. That’s what makes one definition reusable everywhere.
Calculate is the one function that modifies that context:
// Add a filter (intersects with whatever filters are already active)OpenSRCount := Calculate( Count(ServiceRequest.Id), ServiceRequest.Status = "Open")
// Remove filters — the classic %-of-total denominatorPctOfTotalRevenue := Divide( [Revenue], Calculate([Revenue], RemoveFilters(Product, Customer)))- A bare predicate adds a filter. It intersects with the outer context —
if the dashboard filters Region to “East” and your measure adds
Region = "West", the result is empty, not overridden. RemoveFilters(…)lifts the named dimensions’ user filters (and their grouping), which is how you get totals.RemoveFilters()with no arguments is the grand total.- To override a filter rather than intersect with it, remove then add — removals always apply first, regardless of the order you write them:
// West revenue regardless of any dashboard Region filterWestRevenue := Calculate([Revenue], Sales.Region = "West", RemoveFilters(Sales.Region))One important guarantee: RemoveFilters can never touch row-level security.
RLS filters are structural and sit beneath everything a measure can express.
Fixed-grain (LOD) measures
Section titled “Fixed-grain (LOD) measures”Fixed evaluates an expression at an explicit grain, regardless of the
chart’s grouping — Tableau users know this as a FIXED level-of-detail
expression:
CustomerRevenue := Fixed(Customer.Id, [Revenue])
// Date columns take an inline grain — no truncation boilerplateCustomersPerMonth := Fixed(Date.Date:month, CountDistinct(Sales.CustomerId))Put [CustomerRevenue] on a by-region chart with aggregation avg and you get
average revenue per customer per region. One deliberate difference from
Tableau: SquareX Fixed respects the filters active in the view — filters
“just work” on it, no context-filter promotion required.
Time intelligence
Section titled “Time intelligence”// Same period last yearRevenueLY := Calculate([Revenue], DateShift(Date.Date, -1, "year"))
// Year-to-date accumulationRevenueYTD := YTD([Revenue], Date.Date)
// 12-month rolling total (missing months count as zero, not skipped)Revenue12mRolling := RollingSum([Revenue], 12, "month")Combine them for the standard comparison set: [Revenue], [RevenueLY], and
Divide([Revenue] - [RevenueLY], [RevenueLY]) for year-over-year growth.
One correctness guard worth knowing: YTD and RollingSum only accept
additive expressions (sums and counts, and combinations of them). Asking
for YTD([AvgOrderValue]) is rejected — a running sum of ratios is silently
wrong — and the fix that says what you mean is
Divide(YTD([Revenue]), YTD([Orders])).
Branching and variables
Section titled “Branching and variables”// Searched conditions — first true wins, no SWITCH(TRUE()) idiom neededRevenueTier := Case( [Revenue] >= 1000000, "Tier 1", [Revenue] >= 100000, "Tier 2", "Tier 3")
// Match a valueOrderVolumeBand := Switch([Orders], 0, "None", 1, "Single", "Multi")
// Name a sub-expression once, compute it onceMarginPct := Let(rev, [Revenue], Divide(rev - Sum(Sales.Cost), rev))Iif(cond, then, else) covers the simple two-branch case, and // comments
work anywhere — measures can document themselves.
Two ways to author, one language
Section titled “Two ways to author, one language”In the model editor, every measure can be authored two ways:
- Template builder — forms for the common shapes: aggregation, ratio, % of total, filtered measure, time comparison, to-date, rolling window, fixed-grain, branching. No syntax to learn; the builder shows the generated code as you go.
- Code editor — full SquareX with autocomplete (tables, columns, measures, functions), inline diagnostics as you type, format-on-save, and a compiled-SQL preview so you can see exactly what will run.
Both edit the same definition. A measure built with templates opens in the
builder; one that uses code-only shapes (like Let) shows a read-only summary
with an Edit as code path.
Things SquareX deliberately doesn’t have
Section titled “Things SquareX deliberately doesn’t have”- No row context. Everything in a measure is aggregate-level; a bare column outside an aggregation is an error that points you to calculated fields, which is where row-level expressions live. This removes the single most confusing concept in DAX.
- No 250-function catalog. The surface is ~25 curated functions; the escape hatch for exotic needs is SQL, not more functions.
- SQL semantics for NULL and types, not invented ones: aggregates skip
NULLs,
NULL + 5is NULL (useCoalesce([Measure], 0)when you want zeros), and there’s no implicit string↔number coercion — format numbers withToTextbefore concatenating with&.
Related
Section titled “Related”- Syntax & grammar — references, operators, comments.
- Filter context & evaluation — how measures evaluate, and the NULL/type rules.
- Function reference — every function, one page each.
- Diagnostics — SQX001–SQX015 with fixes.
- Worked examples — the acceptance corpus, annotated.
- Calculated fields — row-level expressions, the layer beneath measures.