In an order lines table, order_id repeats once per line and line_number repeats in every order.
Neither is unique on its own; the pair is. The built-in unique test checks one column, so a key made
of several columns needs unique_combination_of_columns from the dbt_utils package.
Copy and paste
1. Add the package, in packages.yml at the root of the project:
packages:
- package: dbt-labs/dbt_utils
version: [">=1.4.0", "<2.0.0"]
dbt deps
2. Add the test to the model, at model level rather than under a column:
# models/marts/_marts.yml
version: 2
models:
- name: order_lines
data_tests:
- dbt_utils.unique_combination_of_columns:
arguments:
combination_of_columns: [order_id, line_number]
dbt test -s order_lines
Without a package
unique on an expression. A model-level unique test accepts an expression as column_name:
models:
- name: order_lines
data_tests:
- unique:
arguments:
column_name: "(order_id || '-' || line_number)"
The separator matters: without it, order 1 line 12 and order 11 line 2 both become 112.
A singular test, in tests/assert_order_lines_unique.sql:
select order_id, line_number, count(*) as copies
from {{ ref('order_lines') }}
group by order_id, line_number
having count(*) > 1
The test fails if the query returns any row; see how to write a custom SQL test.
What the test catches
In the example project two orders share order_id 5 but have different customers. The tests on
stg_orders give:
| Test | Result |
|---|---|
unique on order_id |
FAIL 1 |
unique_combination_of_columns on order_id, customer_id |
PASS |
Both are right: order_id alone repeats, the pair does not. Which one you need depends on what the
table’s grain is supposed to be.
Tested with dbt Core 1.12 and dbt_utils 1.4.1.