Snapshots: история изменений (SCD type 2)

Источник хранит только текущее состояние строки — snapshot запоминает, как оно менялось во времени.

Snapshot — механизм dbt, реализующий SCD type 2: при каждом запуске он сравнивает источник с сохранённой историей и фиксирует изменившиеся строки с интервалами их действия (с какого по какое время версия была актуальной).

Проблема исчезающей истории

В таблице клиентов поле status сегодня active, завтра churned. Источник просто перезапишет значение — и вы навсегда потеряете информацию, когда клиент ушёл. А бизнесу нужно отвечать на вопросы «сколько клиентов было активно в марте?», «какой тариф был у клиента в момент покупки?». Для этого нужна история — её и сохраняет snapshot.

SCD type 2: версии строк

Slowly Changing Dimension type 2 — классический приём хранилищ: вместо перезаписи строки заводят новую версию, а старую закрывают, проставив интервал действия. Так у одной сущности появляется история версий.

customer_id | status   | dbt_valid_from | dbt_valid_to
   42       | active   | 2026-01-01     | 2026-03-15   <- старая версия (закрыта)
   42       | churned  | 2026-03-15     | NULL         <- текущая (valid_to = NULL)

Строка с dbt_valid_to = NULL — актуальная. По интервалам можно «отмотать» состояние на любую дату.

Объявление snapshot

-- snapshots/customers_snapshot.sql
{% snapshot customers_snapshot %}
{{ config(
    target_schema='snapshots',
    unique_key='customer_id',
    strategy='timestamp',
    updated_at='updated_at'
) }}
select * from {{ source('raw', 'customers') }}
{% endsnapshot %}
unique_keyкак опознать ту же сущность между запусками
strategy='timestamp'изменение определяется по полю времени
updated_atполе, по которому видно, что строка изменилась

Альтернативная стратегия — check: dbt сам сравнивает указанные колонки, если в источнике нет надёжного updated_at.

Запуск

# Снять очередной снимок (зафиксировать изменения)
dbt snapshot

Snapshot запускают регулярно. Каждый запуск ловит то, что изменилось с прошлого раза, — поэтому важно не пропускать запуски, иначе промежуточные состояния потеряются.

Как работает под капотом

При запуске dbt берёт текущий источник и сравнивает с таблицей snapshot по unique_key. Если строка новая — вставляет с valid_from = now, valid_to = NULL. Если существующая строка изменилась (по timestamp или check) — у старой версии проставляет valid_to = now (закрывает интервал) и вставляет новую версию. Неизменившиеся строки не трогает. Так в таблице накапливается полная история, а служебные поля dbt_valid_from/to задают временные интервалы.

Частые ошибки

  • Запускать snapshot редко/нерегулярно. Изменения между запусками теряются — snapshot ловит только разницу с прошлым снимком.
  • Неверный unique_key. Если ключ не опознаёт сущность, история «склеится» или раздробится.
  • Использовать snapshot для того, что должно быть моделью. Snapshot — про историю изменений, а не про трансформацию.

Итоги

  • Snapshot реализует SCD type 2: сохраняет историю версий строк с интервалами dbt_valid_from/to.
  • Стратегия timestamp или check определяет, как dbt замечает изменения.
  • Запускать регулярно: snapshot ловит лишь разницу с прошлым снимком.
Проверьте себя
1. Что реализуют snapshots в dbt?
AРезервное копирование базы
BSCD type 2 — историю изменений строк с интервалами действия версий
CУскорение запросов
DШифрование данных
2. Что означает строка snapshot с dbt_valid_to = NULL?
AСтрока удалена
BЭто текущая, актуальная версия записи
CОшибка данных
DСамая старая версия
3. Почему snapshot важно запускать регулярно?
AИначе он удалит данные
BОн ловит только разницу с прошлым снимком — промежуточные изменения иначе теряются
CИначе сломается DAG
DЭто ускоряет dbt run