Many source tables keep only the current state: when a customer moves to another country, the old value is gone. A snapshot records every version of each row, with the period in which it was valid. That is a slowly changing dimension of type 2, and it lets you answer questions such as “which country was this customer in when they ordered?”.

Copy and paste

# snapshots/customers_snapshot.yml
snapshots:
  - name: customers_snapshot
    relation: source('raw', 'customers')
    config:
      schema: snapshots
      unique_key: customer_id
      strategy: timestamp
      updated_at: updated_at
      hard_deletes: invalidate
dbt snapshot                            # all snapshots
dbt snapshot -s customers_snapshot      # one snapshot

Without a reliable updated_at column, compare the values instead:

    config:
      unique_key: customer_id
      strategy: check
      check_cols: [name, country]

Read the current version of each row:

select * from {{ ref('customers_snapshot') }} where dbt_valid_to is null

What the snapshot stores

Customer 11 moved from Germany to France on 20 May, and customer 12 was deleted from the source. After three snapshot runs:

customer_id country dbt_valid_from dbt_valid_to
10 IT 2026-01-01 00:00
11 DE 2026-01-02 00:00 2026-05-20 08:00
11 FR 2026-05-20 08:00
12 IT 2026-01-03 00:00 time of the run that saw the delete

The current version of each row has dbt_valid_to empty. dbt also adds dbt_scd_id, a unique ID for each version, and dbt_updated_at.

The strategies

Hard deletes

hard_deletes decides what happens when a row disappears from the source:

Things to know

Tested with dbt Core 1.12.

References