A measure is one aggregation, such as sum("Sales"., written once on a semantic model and used by every chart that shows it. The chart supplies the rows, the joins and the filters; the measure supplies the calculation, and this page explains the rules behind that split.
FROM, JOIN, WHERE or GROUP BY; the chart supplies all four.LEFT ANY JOIN, whatever cardinality the relationship stores. The join keeps at most one row from a joined table per fact row, so it can never multiply your result; when the joined key is not unique, the one matching row it picks is arbitrary.sumIf("Sales". "Amount", "Sales". "Region" = 'West'), countIf("Sales". "Status" = 'Returned'). A condition wrapped around an aggregate, such as if(cond, sum(. . . ), 0), fails when you save.sum(sum("Sales". "Amount")) OVER ().PARTITION BY ALL stands in for the chart's grouping. Per window it becomes the chart's group-by columns minus that window's ORDER BY columns, or tuple() when none remain, so one measure adapts to every chart.ORDER BY names the chart's period columns as bare names, for example ORDER BY "Year". A scalar-wrapped column does not give a monthly window on a monthly chart, and a measure name written there is read as a group-by column instead: write the aggregate out, as in rank() OVER (ORDER BY sum("Sales". "Amount") DESC).PARTITION BY ALL with a metric ORDER BY: the macro keeps every group-by column, so each partition holds one row and every rank comes out 1. A PARTITION BY ALL window with no ORDER BY gives one row per partition as well.round("Revenue", 2).lagInFrame window over PARTITION BY "Month" ORDER BY "Year" reads the same month of the previous year.sum("Revenue") OVER (PARTITION BY ALL ORDER BY "Month" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW).PARTITION BY ALL is a DataPrism macro, not part of the query language. Copying a measure's SQL into the SQL editor fails there.
QUALIFY step) unless every involved window already partitions by that column. That is why a share of a total is read against the unfiltered total.sum and count totals match the sum of the rows; a ratio, an average or a distinct count does not.lagInFrame shape."Gross Profit" - "Operating Cost", round("Revenue", 2)), conditions over measures, and a non-windowed measure as the argument of a new window (it inlines to an aggregate).ORDER BY or PARTITION BY (write the aggregate out), and windowing a windowed measure again.whole_number, decimal_number, date, date_time, time, duration, boolean or text. The type comes from the expression's result and becomes the measure's value type.Map result, for example, is refused.toFloat64(sum("Sales". "Amount")) / nullIf(count(), 0). Without nullIf, a zero denominator gives inf, nan or a failed query instead of an empty value.count("Sales". "Customer Id") counts the rows where that column is not empty; count() counts rows.Float64 rather than between Decimal values when you want fractional percentages: a Decimal division keeps a fixed scale, so 12.345 % can show as 12.00.These mistakes return a number without any error. Check them before you trust a result:
toFloat64(sum("Sales". "Amount")) / nullIf(count(), 0), or weight it.count() counts fact rows, never the many side of a relationship.nullIf on the denominator, a zero leaks as inf or nan.AMBIGUOUS_IDENTIFIER); qualify the reference with its table.PARTITION BY ALL without ORDER BY: every partition holds one row.sumIf, or a latest-period pattern.today(); a literal date freezes while the measure is compiled again on every chart run.The window combines PARTITION BY ALL with a metric ORDER BY. The macro keeps every group-by column, so each partition holds exactly one row and every rank is 1. Write the ranked aggregate out instead, without the macro: rank() OVER (ORDER BY sum("Sales"..
A total is computed by running the query again at its own, coarser grain, with the summed-out group-by fields removed, not by adding the rows. Plain sum and count totals match the sum of the rows; ratios, averages and distinct counts do not. See Matrix visual.
By design: windows see the data before your filters, so a share of total is computed against the unfiltered total. A filter on a group-by column is applied after the windows unless every involved window already partitions by that column.
The dimension's key is not unique, so the join picked one of several matching rows for that fact row. Make the key unique, or read the value from a dimension whose key is unique.
Windows run after aggregation, and a window inside another window is illegal. A windowed measure may be post-processed with scalar functions only, such as round("Revenue", 2). To change what the window computes, edit the window inside that measure.
A card has no group-bys, so a windowed measure is collapsed onto a hidden timeline: numeric values are summed across the qualifying periods. A window shape that stays constant across the timeline is multiplied by the number of periods on a card. Filter the timeline columns on the card, or keep the measure additive, such as a lagInFrame shape. See KPI and Cards.
One aggregate sits inside another, directly or through a reference that inlines into it. Windows run after aggregation, so let the window wrap the aggregate: sum(sum("Sales".. A comparison against a windowed value goes in an outer if() on the grouped row.
The wrong-pattern list on this page covers them all: a non-unique dimension key, an average of averages, counting over a fan-out, added distinct counts, a division without nullIf, an ambiguous bare column, PARTITION BY ALL without ORDER BY, a share read under a filter, a truncated unsorted result, totals read as sums, a period shift over missing periods, a snapshot summed across dates, and a literal date for a relative period.
One of these: whole_number, decimal_number, date, date_time, time, duration, boolean or text. A result whose type is not one of them is refused when you save, and the message names the type.
An aggregate cannot sit inside another aggregate, and windows run after aggregation, so the latest period cannot be picked inside the sum itself. Two patterns work:
if("Month" = max("Month") OVER (ORDER BY "Month" ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), sum("Sales". "Amount"), 0). On a card the pattern collapses to the latest period's value. A filter on the period column hides its row, so the measure reads 0 instead of moving the latest period.Is Current Month, with sumIf("Sales". "Amount", "Sales". "Is Current Month" = 1).No. A referenced name is substituted with its expression, so a helper used by two measures is computed as part of each of them, not once and shared. The chart declares the selected measures and everything they reference, sorted by name, so a helper appears in the query only when a shown measure reaches it.
Yes, qualify a column with its BI table name, such as "Sales".. A bare name keeps every joined table that has such a column, and as soon as two of them share it the reference becomes ambiguous (AMBIGUOUS_IDENTIFIER). Two things stay bare: the period columns in a window's ORDER BY and PARTITION BY, which name the chart's output columns, and a referenced measure, which is always written by its own name.