In the legacy format, a measure aggregated a column and a metric pointed at the measure. The latest YAML
spec removes the middle step: a type: simple metric carries the aggregation itself, with agg and
expr. “Measures” in this section means exactly that.
Copy and paste
metrics:
- name: revenue
type: simple
agg: sum
expr: amount
- name: order_count
type: simple
agg: count
expr: 1 # one per row
- name: ordering_customers
type: simple
agg: count_distinct
expr: customer_id
- name: average_order_amount
type: simple
agg: average # not avg
expr: amount
- name: smallest_order
type: simple
agg: min
expr: amount
- name: largest_order
type: simple
agg: max
expr: amount
- name: median_order
type: simple
agg: median
expr: amount
mf query --metrics order_count,ordering_customers,revenue,average_order_amount,smallest_order,largest_order,median_order \
--group-by metric_time__year
| Year | order_count | ordering_customers | revenue | average_order_amount | smallest_order | largest_order | median_order |
|---|---|---|---|---|---|---|---|
| 2025 | 12 | 4 | 1,340 | 111.67 | 60 | 200 | 105 |
| 2026 | 7 | 5 | 890 | 127.14 | 50 | 300 | 110 |
The aggregations
agg |
Returns | Adds up across groups? |
|---|---|---|
sum |
total of expr |
yes |
count |
number of non-null values of expr; expr: 1 counts rows |
yes |
count_distinct |
number of different values | no |
average |
mean of expr |
no |
min, max |
smallest, largest value | no (but min of mins works) |
median |
middle value | no |
percentile |
see percentile measures | no |
sum_boolean |
number of rows where a boolean expr is true |
yes |
The keyword is average: agg: avg stops the parse with Invalid enum value: `avg` in enum
AggregationType. expr defaults to the metric’s name, so a metric called
amount with agg: sum would sum the amount column; writing expr explicitly is clearer.
Non-additive measures are computed, not summed
The Semantic Layer computes each metric from the rows at the grain you query, so non-additive measures stay correct: 2025 has 4 ordering customers and 2026 has 5, but over both years there are 5, not 9, because four of them ordered in both years. The same holds for averages and medians. What is not safe is adding up such values afterwards, in a spreadsheet or a BI total row.
expr is SQL
expr can be any expression on the model’s columns, evaluated row by row before the aggregation:
amount - discount, case when ... end, a boolean for sum_boolean. It is pasted into the generated
SQL, so write it in your warehouse’s dialect. For metrics that count only some rows, see
how to count rows that match a condition.
Ratios are not measures
“Average order value” can be an average of amount, and here it is the same number, but as a
ratio metric of revenue over
orders it can also combine metrics with different filters or from different models. Use average for a
plain mean of a column, and a ratio when the numerator and the denominator are business metrics of their
own.
Tested with dbt Core 1.12 and MetricFlow 0.213 on DuckDB.