Skip to main content

Metrics reference

A metric is a value that describes or summarizes features from a collection of data points. For example, Num unique user ids is a metric. It describes the unique number of user_ids in a collection of user_id data points.

In Lightdash, metrics are used to summarize dimensions or, sometimes, other metrics.


Adding metrics to your project using the meta tag.

1. Using the column meta tag (Suggested)

To add a metric to Lightdash using the meta tag, you define it in your dbt project under the dimension name you're trying to describe/summarize.


models:
- name: my_model
columns:
- name: user_id # dimension name of your metric
meta:
metrics:
num_unique_user_ids: # name of your metric
type: count_distinct # metric type
num_user_ids:
type: count

Once you've got the hang of what these metrics look like, read more about the metric types you can use below.

2. Using the model meta tag

Sometimes a metric references many columns, in these cases you can define the metric at the model level:

version: 2

models:
- name: my_model
meta:
metrics:
num_unique_user_ids:
type: count_distinct
sql: ${TABLE}.user_id

Metric Categories

Each metric type falls into one of these categories. The metric categories tell you whether the metric type is an aggregation and what type of fields the metric can reference:

Aggregate metrics

Aggregate metric types perform (surprise, surprise) aggregations. Sums and averages are examples of aggregate metrics: they are measurements summarizing a collection of data points.

Aggregate metrics can only reference dimensions, not other metrics.

Non-aggregate metrics

Non-aggregate metrics are metric types that, you guessed it, do not perform aggregations.

Numbers and booleans are examples of non-aggregate metrics. These metric types perform a calculation on a single data point, so they can only reference aggregate metrics. They cannot reference dimensions.

Metric configuration

You can customize your metrics in your dbt model's YAML file. Here's an example of the properties used in defining a metric:

version: 2

models:
- name: sales_stats
meta:
joins:
- join: web_sessions
sql_on: ${web_sessions.date} = ${sales_stats.date}
group_details:
product_details:
label: Product Details
description: 'Fields that have information about the products in the basket.'
item_details:
label: Item Details
description: 'Fields that have information about the items in the basket.'
columns:
- name: revenue
description: 'Total estimated revenue in GBP based on forecasting done by the finance team.'
meta:
metrics:
total_revenue:
label: 'Total revenue GBP'
type: SUM
description: 'Total revenue in GBP'
sql: 'IF(${revenue} IS NULL, 10, ${revenue})'
groups: ['product_details', 'item_details'] # this would add the metric to a nested group: `product details` --> `item details`
hidden: false
round: 0
format: 'gbp'
show_underlying_values:
- revenue
- forecast_date
- web_sessions.session_id # field from joined table
filters:
- is_adjusted: true

Here are all of the properties you can customize:

PropertyRequiredValueDescription
labelNostringCustom label. This is what you'll see in Lightdash instead of the metric name.
typeYesmetric typeMetrics must be one of the supported types.
descriptionNostringDescription of the metric that appears in Lightdash. A default description is created by Lightdash if this isn't included
sqlNostringCustom SQL used to define the metric.
hiddenNobooleanIf set to true, the metric is hidden from Lightdash. By default, this is set to false if you don't include this property.
roundNonumberRounds a number to a specified number of digits
formatNostringThis option will format the output value on the result table and CSV export. Currently supports one of the following: ['km', 'mi', 'usd', 'gbp', 'eur', 'percent', 'id']
compactNostringThis option will compact the number value (e.g. 1,500 to 1.50K). Currently supports one of the following: ['thousands', 'millions', 'billions', 'trillions']
groupsNostring or string[]If you set this property, the metric will be grouped in the sidebar with other metrics with the same group label.
urlsNoArray of { url, label }Adding urls to a metric allows your users to click metric values in the UI and take actions, like opening an external tool with a url, or open at a website. You can use liquid templates to customise the link based on the value of the dimension.
show_underlying_valuesNoArray of dimension namesYou can limit which dimensions are shown for a field when a user clicks View underlying data. The list must only include dimension names from the base model or from any joined models.
filtersNoArray of {filter field: value}You can add filter logic to limit the values included in the metric calculation. You can add many filters. See which filter types are supported here.

Metric types

TypeCategoryDescription
percentileAggregateGenerates a percentile of values within a column
medianAggregateGenerates the 50th percentile of values within a column
averageAggregateGenerates an average (mean) of values within a column
booleanNon-aggregateFor fields that will show if something is true or false
countAggregateCounts the total number of values in the dimension
count_distinctAggregateCounts the total unique number of values in the dimension
dateNon-aggregateFor adding calculations to metrics that return dates.
maxAggregateGenerates the maximum value within a numeric column
minAggregateGenerates the minimum value within a numeric column
numberNon-aggregateFor adding calculations to metrics that return numbers.
stringNon-aggregateFor metrics that contain letters or special characters
sumAggregateGenerates a sum of values within a column

percentile

Takes the percentile of the values in the given field. Like SQL's PERCENTILE_CONT function.

The percentile metric can be used on any numeric dimension or, for custom SQL, any valid SQL expression that gives a numeric table column.

For example, this creates a metric median_price by taking the 50% percentile of the item_price dimension:

columns:
- name: item_price
meta:
metrics:
median_price:
type: percentile
percentile: 50

median

Takes the 50th percentile of the values in the given field. Like SQL's PERCENTILE_CONT(0.5) function.

The median metric can be used on any numeric dimension or, for custom SQL, any valid SQL expression that gives a numeric table column.

For example, this creates a metric median_price by taking the 50% percentile of the item_price dimension:

columns:
- name: item_price
meta:
metrics:
median_price:
type: median

average

Takes the average (mean) of the values in the given field. Like SQL's AVG function.

The average metric can be used on any numeric dimension or, for custom SQL, any valid SQL expression that gives a numeric table column.

For example, this creates a metric avg_price by taking the average of the item_price dimension:

columns:
- name: item_price
meta:
metrics:
avg_price:
type: average

boolean

Tells you whether something is True or False.

The boolean metric can be used on any valid SQL expression that gives you a TRUE or FALSE value. It can only be used on aggregations, which means either aggregate metrics or custom SQL that references other metrics. You cannot build a boolean metric by referencing other unaggregated dimensions from your model.

boolean metrics don't do any aggregations; they just reference other aggregations.

For example, the avg_price metric below is an average of all of the item_price values in our product table. A second metric called is_avg_price_above_20 is a boolean type metric. The is_avg_price_above_20 metric has a custom SQL expression that tells us whether the avg_price value is greater than 20.

columns:
- name: item_price
meta:
metrics:
avg_price:
type: average
is_avg_price_above_20:
type: boolean
sql: 'IF(${avg_price} > 20, TRUE, FALSE)'

count

Does a table count, like SQL’s COUNT function.

The count metric can be used on any dimension or, for custom SQL, any valid SQL expression that gives a set of values.

For example, this creates a metric number_of_users by counting the number of user_id values in the table:

columns:
- name: user_id
meta:
metrics:
number_of_users:
type: count

count_distinct

Counts the number of distinct values in a given field. It's like SQL’s COUNT DISTINCT function.

The count_distinct metric can be used on any dimension or, for custom SQL, any valid SQL expression that gives a set of values.

For example, this creates a metric number_of_unique_users by counting the number of unique user_id values in the table:

columns:
- name: user_id
meta:
metrics:
number_of_unique_users:
type: count_distinct

date

Gives you a date value from an expression.

The date metric can be used on any valid SQL expression that gives you a date value. It can only be used on aggregations, which means either aggregate metrics or custom SQL that references other metrics. You cannot build a date metric by referencing other unaggregated dimensions from your model.

Creating a max or min date metric with type: date

If you want to create a metric of a maximum or minimum date, you can't use type: max or of type: min metrics because these are only compatible with numeric type fields. Instead, you can calculate a maximum or minimum date by defining a metric of type: date and using some custom sql, like this:

- name: created_at_date
meta:
dimension:
type: date
metrics:
max_created_at_date:
type: date
sql: MAX(${TABLE}.created_at_date)

max

Max gives you the largest value in a given numeric field. It's like SQL’s MAX function.

The max metric can be used on any numeric dimension or, for custom SQL, any valid SQL expression that gives a numeric value.

Because type: max metrics only work with numerical fields, you can't use them to find a maximum date. Instead, you can use the MAX() function in the sql parameter of a metric of type: date to get a maximum date (you can see an example of this in the date section.

For example, this creates a metric max_delivery_cost by looking at the delivery_cost dimension and taking the largest value it finds:

columns:
- name: delivery_cost
meta:
metrics:
max_delivery_cost:
type: max

min

Min gives you the smallest value in a given numeric field. It's like SQL’s MIN function.

The min metric can be used on any numeric dimension or, for custom SQL, any valid SQL expression that gives a numeric value.

Because type: min metrics only work with numerical fields, you can't use them to find a minimum date. Instead, you can use the MIN() function in the sql parameter of a metric of type: date to get a minimum date (you can see an example of this in the date section.

For example, this creates a metric min_delivery_cost by looking at the delivery_cost dimension and taking the smallest value it finds:

columns:
- name: delivery_cost
meta:
metrics:
min_delivery_cost:
type: min

number

Used with numbers or integers. A number metric doesn't perform any aggregation but can be used to perform simple transformations on other metrics.

The number metric can be used on any valid SQL expression that gives you a numeric or integer value. It can only be used on aggregations, which means either aggregate metrics or custom SQL that references other metrics. You cannot build a number metric by referencing other unaggregated dimensions from your model.

For example, this creates a metric called total_gross_profit_margin_percentage based on the total_sale_price and total_gross_profit_margin aggregate metrics:

columns:
- name: sale_price
meta:
metrics:
total_sale_price:
type: sum
- name: gross_profit_margin
meta:
metrics:
total_gross_profit_margin:
type: sum
total_gross_profit_margin_percentage:
type: number
sql: '(${total_gross_profit_margin}/ NULLIF(${total_sale_price},0))'

The example above also uses the NULLIF() SQL function to avoid division-by-zero errors.

sum

Adds up the values in a given field. Like SQL’s SUM function.

The sum metric can be used on any numeric dimension or, for custom SQL, any valid SQL expression that gives a numeric table column.

For example, this creates a metric total_revenue by adding up the values in the revenue dimension:

columns:
- name: revenue
meta:
metrics:
total_revenue:
type: sum

string

Used with fields that include letters or special characters.

The string metric can be used on any valid SQL expression that gives you a string value. It can only be used on aggregations, which means either aggregate metrics or custom SQL that references other metrics. You cannot build a string metric by referencing other unaggregated dimensions from your model.

string metrics are rarely used because most SQL aggregate functions don't return strings. One common exception is MySQL’s GROUP_CONCAT function.

For example, this creates a metric product_name_group by combining the unique values of a dimension called product_name:

columns:
- name: product_name
meta:
metrics:
product_name_group:
type: string
sql: 'GROUP_CONCAT(${TABLE}.product_name)'

Adding your own metric descriptions

We add default descriptions to all of the metrics you include in your model. But, you can override these using the description parameter when you define your metric.

metrics:
num_user_ids:
type: count
description: 'Total number of user IDs. NOTE: this is NOT counting unique user IDs'

Using custom SQL in aggregate metrics

You can include custom SQL in your metric definition to build more advanced metrics using the sql parameter. Inside the sql parameter, you can reference any other dimension from the given model and any joined models. You can’t reference other metrics.

You can reference dimensions from the same model like this: sql: "${dimension_in_this_model}" Or from joined models like this: sql: "${other_model.dimension_in_other_model}"

metrics:
num_unique_7d_web_active_user_ids:
type: count_distinct # metric type
sql: 'IF(${is_7d_web_active}, ${user_id}, NULL)'
num_unique_paid_user_ids:
type: count_distinct
sql: 'IF(${subscriptions.is_active}, ${user_id}, NULL)'

Using custom SQL in non-aggregate metrics

In non-aggregate metrics, you can reference any other metric from the given model and any joined models. You can’t reference other dimensions.

You can reference metrics from the same model like this: sql: "${metric_in_this_model}" Or from joined models like this: sql: "${other_model.metric_in_other_model}"

metrics:
num_unique_users:
type: count_distinct
is_num_unique_users_above_100:
type: boolean
sql: 'IF(${num_unique_users} > 100, TRUE, FALSE)'
percentage_user_growth_daily:
type: number
sql: '(${num_unique_users} - ${growth_model.num_unique_users_lag_1d}) / NULLIF(${growth_model.num_unique_users_lag_1d}, 0)'

Show underlying values

By default, we show all of the dimensions from the Table when you click View underlying data. If you have fields from a joined table included in your results table, then we'll also show you all of the fields from the joined Table.

You can limit which dimensions are shown for a field when a user clicks View underlying data by adding the list of dimensions to your .yml files:

version: 2

models:
- name: sales_stats
meta:
joins:
- join: web_sessions
sql_on: ${web_sessions.date} = ${sales_stats.date}
columns:
- name: user_id
description: 'Unique ID for users.'
meta:
dimension:
type: string
metrics:
count_users:
type: count_distinct
show_underlying_values:
- revenue_gbp_total_est
- actual_date
- web_sessions.session_id # field from joined table

The list of fields must be made of dimension names (no metrics) from the base table or from any joined tables. To reference a field from a joined table, you just need to prefix the dimension name with the joined table name, like this: my_joined_table_name.my_dimension.

The order that the fields are listed in show_underlying_values is the order that they'll appear in on the view underlying data table.

Compact values

You can compact values in your YAML. For example, if I wanted all of my revenue values to be shown in thousands (e.g. 1,500 appears as 1.50K), then I would write something like this in my .yml:

    version: 2
models:
- name: sales
columns:
- name: revenue
meta:
dimension:
compact: thousands # You can also use 'K'
ValueAliasExample output
thousands"K" and "thousand"1K
millions"M" and "million"1M
billions"B" and "billion"1B
trillions"T" and "trillion"1T

Groups

You can group your dimensions and metrics in the sidebar using the groups parameter.

To do this, you need to set up group_details in the model's configuration. Then, you can use these groups to organize metrics and dimensions. You can create nested groups up to 2 levels.

version: 2
models:
- name: baskets
meta:
group_details:
product_details:
label: Product Details
description: 'Fields that have information about the products in the basket.'
item_details:
label: Item Details
description: 'Fields that have information about the items in the basket.'

columns:
- name: basket_item_id
description: 'ID for the product item within the basket.'
meta:
dimension:
groups: ['product_details', 'item_details'] # this would add the dimension to a nested group: `product details` --> `item details`
metrics:
count_total_basket_items:
type: count_distinct
groups: ['product_details', 'item_details'] # this would add the metric to a nested group: `product details` --> `item details`
- name: product_name
description: 'Full name of the product.'
meta:
dimension:
label: 'Product name'
groups: ['product_details'] # this would add the dimension under the group label: `product_details`
metrics:
count_total_product_types:
type: count_distinct
groups: ['product_details'] # this would add the metric under the group label: `product_details`

Filters

Filters are applied to metrics any time that metric is used in Lightdash. Filters can only be used with aggregate metric types.

For example, we could add a filter to our users count to make sure it didn't include user IDs with closed accounts, like this:

version: 2

models:
- name: sales_stats
columns:
- name: user_id
description: 'Unique ID for users.'
meta:
dimension:
type: string
metrics:
count_users:
type: count_distinct
filters:
- is_closed_account: false

These filters do not appear in the Filters tab in the Explore view, instead, they are applied automatically in the SQL query that fetches your results. That means filters added using the filter parameter can't be removed in the UI and won't be visible to users unless they look at the SQL query.

info

To use special characters such as %!_> in your filter value you can either put the value in quotes, or escape special characters with a backslash \. For example, if you wanted to filter for subscription status of is_subscribed you can write the metric in one of these ways:

filters:
- subscription_status: 'is_subscribed'
filters:
- subscription_status: is\_subscribed

Available filter types

TypeExample (in English)Example (as code)
isUser name is equal to katieuser_name: "katie"
is notUser name is not equal to katieuser_name: "!katie"
containsUser name contains katieuser_name: "%katie%"
does not containUser name does not contain katieuser_name: "!%katie%"
starts withUser name starts with katieuser_name: "katie%"
ends withUser name ends with katieuser_name: "%katie"
is greater than (number)Number of orders is greater than 4num_orders: "> 4"
in the past (date) (interval)Date is before x (days / months / years)date: "inThePast 14 months"
in the next (date) (interval)Date is after x (days / months / years)date: "inTheNext 14 days"
is greater than or equal toNumber of orders is greater than or equal to 4num_orders: ">= 4"
is less thanNumber of orders is less than 4num_orders: "< 4"
is less than or equal toNumber of orders is less than or equal to 4num_orders: "<= 4"
is nullStatus is NULLstatus: "null"
is not nullStatus is not NULLstatus: "!null"
is [boolean]Is complete is trueis_complete: "true"
is not [boolean]Is complete is false or nullis_complete: "!true"
info

To filter a field by a set of given values you can supply them as an array for that field. For example, if you wanted to filter for orders with order status completed or shipped you should write the metric like:

columns:
- name: order_id
meta:
metrics:
completed_or_shipped_order_count:
type: count_distinct
filters:
- order_status:
- completed
- shipped

If you have many filters in your list, they will be joined using AND.

For example:

filters:
- is_closed_account: false
- is_7d_active: true

Would give you logic like is_closed_account = TRUE AND is_7d_active = FALSE.

You can filter using fields from either the base model, or any joined models.

To filter using a field from a joined model, just use the syntax model_name.field, like this:

version: 2

models:
- name: sales_stats
meta:
joins:
- join: web_sessions
sql_on: ${web_sessions.date} = ${sales_stats.date}
columns:
- name: user_id
description: 'Unique ID for users.'
meta:
dimension:
type: string
metrics:
count_users:
type: count_distinct
filters:
- is_closed_account: false
- web_sessions.is_bot_user: false

Metric filters cannot be used with non-aggregate metrics

You can't use filters with non-aggregate metric types. Instead, if your non-aggregate metrics are referencing aggregate metric types, you need to apply metric filters to the aggregate metrics.

Here's an example: imagine you wanted to calculate the average cost per item that had the status shipped. You would need to do something like this in your .yml:

version: 2

models:
- name: orders
meta:
metrics:
average_cost_per_item_shipped:
type: number
sql: ${total_cost_of_shipped} / ${count_unique_items_shipped}
columns:
- name: item_id
description: 'Unique ID for items ordered.'
meta:
dimension:
type: string
metrics:
count_unique_items:
type: count_distinct
count_unique_items_shipped:
type: count_distinct
filters:
- status: 'shipped'
- name: item_cost
description: 'Cost for each item ordered.'
meta:
dimension:
type: number
metrics:
total_cost:
type: sum
total_cost_of_shipped:
type: sum
filters:
- status: 'shipped'

Format

You can use the format parameter to have your metrics show in a particular format in Lightdash. These are the options:

OptionDescriptionRaw valueDisplayed value
kmAdds the suffix km to your value1010 km
miAdds the suffix mile to your value1010 mi
usdAdds the $ symbol to your number value10$10.00
gbpAdds the £ symbol to your number value10£10.00
eurAdds the symbol to your number value10€10.00
percentAdds the % symbol and multiplies your value by 1000.1%10
idRemoves commas and spaces from number or string types so that they appear like IDs.12,389,57212389572