A semantic model tells dbt what a table means: which column identifies a row, which columns you can group or filter by, and which metrics it can compute. With that, dbt’s Semantic Layer writes the SQL for questions like “revenue by month by country”, and every tool that asks gets the same answer.

Since dbt 1.12 the definition lives next to the model, in the same YAML block as its columns. The older semantic_models: format with measures still works, but dbt v2 warns about it; see how to fix the “legacy YAML” warning.

Copy and paste

# models/marts/_semantic.yml
version: 2
models:
  - name: orders
    semantic_model:
      enabled: true
    agg_time_dimension: order_date
    columns:
      - name: order_id
        entity:
          type: primary
          name: order
      - name: customer_id
        entity:
          type: foreign
          name: customer
      - name: order_date
        granularity: day
        dimension:
          type: time
      - name: status
        dimension:
          type: categorical
    metrics:
      - name: revenue
        label: Revenue
        type: simple
        agg: sum
        expr: amount
      - name: order_count
        label: Orders
        type: simple
        agg: count
        expr: 1

  - name: customers
    semantic_model:
      enabled: true
    columns:
      - name: customer_id
        entity:
          type: primary
          name: customer
      - name: country
        dimension:
          type: categorical

The Semantic Layer also needs a daily time spine; see how to create a time spine model.

Validate and query on dbt platform:

dbt sl validate
dbt sl query --metrics revenue,order_count --group-by metric_time__month

Or locally with dbt Core and MetricFlow:

pip install "dbt-metricflow[dbt-snowflake]"
dbt parse
mf validate-configs
mf query --metrics revenue,order_count --group-by metric_time__month --order metric_time__month
metric_time__month      revenue    order_count
--------------------  ---------  -------------
2026-01-01T00:00:00         200              2
2026-02-01T00:00:00         250              2
2026-03-01T00:00:00         180              2

The parts

Names in queries

Dimensions are addressed through their entity: order__status, customer__country. Time is grouped with metric_time and a grain: metric_time__day, __week, __month, __quarter, __year. List what a metric supports with:

mf list dimensions --metrics revenue
• customer__country
• metric_time
• order__order_date
• order__status

Next steps

Tested with dbt Core 1.12 and MetricFlow 0.213 on DuckDB; the dbt sl commands come from the dbt platform documentation.

References