---
title: "dbt semantic model keys with no unique test | Metacenta"
description: "MetricFlow trusts a primary entity to be one row per key. Nothing checks it at query time, so a duplicate multiplies every metric. The test that guards it."
url: "https://metacenta.com/rules/semantic-primary-entity-unasserted"
updated: "2026-10-14"
tokens: 632
---

BI semantic layer · rule semantic-primary-entity-unasserted

# dbt semantic model keys with no unique test

A Metacenta review checks this under the rule **Semantic model keys are tested unique**. Everything below applies whether or not you ever commission one.

## What this rule checks

For each dbt semantic model, this rule takes every `primary` or `unique` entity that names a plain column. It passes when a uniqueness test on the model covers that column alone. A `natural` entity is not judged, because it repeats by design.

## Why it matters

MetricFlow assumes one row per key when it joins through the entity. A duplicate key multiplies every metric joined through it, and nothing fails. The number is simply wrong.

## How to fix it

**Test every semantic model key for uniqueness.** Add a `unique` test on each flagged key column, in the model the semantic model reads. A `where` clause on the test stops it counting, so test every row.

Before:

```yaml
semantic_models:
  - name: customers
    model: ref('dim_customers')
    entities:
      - name: customer
        type: primary
        expr: customer_id
```

After:

```yaml
models:
  - name: dim_customers
    columns:
      - name: customer_id
        data_tests:
          - unique
          - not_null
```

## When it is fine to leave

If the model really can hold two rows per value, the entity isn't a primary key. Declare it as `natural` or `foreign` instead. A composite grain test doesn't make any one of its columns unique, so it doesn't pass here.

## What we need to check it

`manifest.json` alone. `unique`, the dbt_expectations uniqueness tests and a one-column `unique_combination_of_columns` count. A test limited by `where` doesn't. An entity built from an expression can't carry a column test, so it's not judged.

[Ask about a review](https://metacenta.com/enquire?src=rule-semantic-primary-entity-unasserted&via=md)

## Related pages

- [dbt semantic models reading unbuilt columns](https://metacenta.com/rules/semantic-column-not-built)
- [Looker views reading tables dbt does not know](https://metacenta.com/rules/lookml-view-unbuilt-table)
- [Looker views reading raw dbt sources](https://metacenta.com/rules/lookml-view-reads-raw-source)
- [Looker derived tables: SQL, native and persistent](https://metacenta.com/rules/lookml-sql-derived-table)
- [A sample review, as a client reads it](https://metacenta.com/sample)
- [All published rules](https://metacenta.com/rules)
- [How a review is assessed](https://metacenta.com/standard)
