Skip to main content
A semantic layer is the business contract between your warehouse and the questions people or agents ask. Its YAML maps physical tables, columns, calculations, and joins to stable names such as conversion_rate, segment, and enterprise_accounts. Use a semantic layer when you want recurring questions such as “How many accounts converted?” and “Which segments are most engaged?” to use the same definitions every time, without rebuilding business logic in each query.

Why use a semantic layer?

Warehouse schemas describe how data is stored. They do not, by themselves, define what your organization means by pipeline, conversion, an active account, or the correct date for a report. The semantic layer records those decisions once and makes them available by name. This makes the layer useful for both self-service analytics and agent-driven workflows:
  • Consistent answers: A semantic request combines a named calculation with declared table grain, base filters, dimensions, and relationship context.
  • Safer query construction: Agents select declared semantic objects instead of inventing physical table and column names.
  • Auditable results: The rendered SQL shows how the semantic request became a warehouse query and provides a starting point for debugging.
  • Controlled flexibility: Named objects are the default, while custom SQL or direct SQL remains an explicit fallback for analysis the layer does not yet cover.
  • Easier evolution: You can add definitions centrally and treat existing names as contracts for downstream questions and workflows.
A semantic layer does not replace warehouse modeling or data-quality checks. It makes the approved meaning and query path explicit on top of that data.

Structure at a glance

A semantic YAML file contains a model and one or more logical tables. At minimum, define the model’s name, description, and tables. Each table needs a name, description, and physical base_table.
Choose one stable entity identifier for the model and set main_id: true on that dimension. Mark unique join keys with unique: true or include them in the table’s primary_key.

Field reference

Model fields

Table fields

Dimensions, facts, metrics, and filters

  • Dimensions and time dimensions require name and data_type. Add expr when the physical column or SQL expression differs from the semantic name.
  • Facts require name and data_type. They represent row-level values and can also use expr.
  • Metrics require name and expr. Metric expressions aggregate rows with functions such as COUNT, SUM, or AVG.
  • Filters require name and expr. A filter expression is a SQL predicate, without the WHERE keyword.
  • All four field types support an optional description and synonyms. Dimensions also support unique and main_id.

Relationships

A relationship connects two logical tables with one column mapping. Composite relationship keys are not supported. Use left_outer or inner for join_type, and many_to_one or one_to_one for relationship_type. For many_to_one, the right-side column must be unique. For one_to_one, both sides must be unique. Use simple semantic column names in relationship_columns.

Funnel events

Define shared funnel events at the model level. Each structured step uses a source_table and either time_dimension or ts_expr. Add filter_expr when the source table contains more than one event type.

Build a semantic layer

1

Confirm each table's grain

Write down what one row represents and identify its primary key. For example, an accounts table may contain one row per account, while an activities table contains one row per event.
2

Choose the main entity ID

Use the most stable account, contact, or user identifier. Set main_id: true on exactly one dimension in the model.
3

Add dimensions and time dimensions

Start with the fields people already use to group and filter reports. Use lowercase snake_case semantic names and describe any field whose meaning is not obvious.
4

Add facts and metrics

Keep row-level values in facts and aggregate calculations in metrics. Guard ratios against division by zero with NULLIF.
5

Add filters and relationships

Use named filters for common conditions. Define a relationship only when the unique side of the join is clear.
6

Validate representative questions

Test dimensions, metrics, filters, relationships, and funnels against direct SQL results before other workflows depend on the model.

Example template

This example models fictional accounts and activities. All identifiers and values are synthetic.

How agents use the layer

An agent first reads the available semantic tables and fields, then submits a small request using their canonical names. Deepline renders the request into the warehouse dialect, executes it, and returns both the result rows and rendered SQL.
For example, this request uses the accounts table and conversion_rate metric defined in the template above:
Start with one metric and a small row limit. Expand the request after confirming the table, metric, dimensions, filters, rendered SQL, and initial rows. If the semantic layer cannot express the question, use the rendered SQL as the safest starting point for an explicit raw-SQL fallback.

Validate and save the layer

Save the YAML in a local file, then run the connector’s update operation with dryRun before writing it.
For Snowflake, dryRun checks that the YAML parses and that each table declares a name and physical table reference. It does not confirm that the referenced warehouse objects exist or compile every semantic definition. Treat it as a first check, not a complete runtime validation. Validate and save the layer in a non-production connection first: change dryRun to false, run representative semantic queries, and compare their results with known direct SQL results. Promote the same validated YAML to the target connection only after those checks pass.
A successful syntax check does not confirm that every physical column, expression, metric, or relationship returns the intended result. Validate the generated queries against your warehouse before using the layer in production workflows.

Validation checklist

  • The YAML parses without unknown or misspelled fields.
  • Every physical table and referenced column exists.
  • Exactly one dimension has main_id: true.
  • Relationship columns use compatible data types, and the “one” side is unique.
  • Metric results match direct SQL for a known test case.
  • Filters return the intended cohort.
  • Time-based results use the expected timezone and date grain.
  • Names are stable, descriptive, and written in lowercase snake_case.

Change definitions safely

Treat logical table, dimension, metric, filter, and funnel step names as query contracts. Prefer additive changes. If a definition’s meaning or grain changes, introduce a new name and validate it before removing the old definition. Keep the last validated YAML available so you can restore it if a new layer produces incorrect SQL or results.

Common errors

Start with the smallest useful layer. Add fields and metrics after the core definitions produce trusted results.