source(): объявление источников

Где заканчивается ваш DAG и начинаются чужие сырые таблицы — это граница source().

source() — Jinja-функция для ссылки на сырые внешние таблицы (которые загрузил не dbt), объявленные в YAML; в отличие от ref(), они не строятся dbt, а только читаются.

Зачем отдельная функция для источников

Модели dbt стоят на фундаменте — сырых таблицах, которые в хранилище положил инструмент загрузки (Fivetran, Airbyte). dbt их не создаёт, но читает. Можно было бы писать from raw.customers напрямую, но это плохо: имя схемы захардкожено, зависимость от источника невидима, и нельзя протестировать «свежесть» данных. source() решает всё это.

Объявление источника в YAML

# models/staging/_sources.yml
version: 2
sources:
  - name: raw            # логическое имя источника
    schema: raw          # реальная схема в хранилище
    tables:
      - name: customers
        description: "Сырые клиенты из CRM"
      - name: orders
        description: "Сырые заказы из биллинга"
        loaded_at_field: created_at
        freshness:        # проверка свежести данных
          warn_after: {count: 12, period: hour}
          error_after: {count: 24, period: hour}

Ссылка на источник в модели

Теперь вместо хардкода raw.customers в модели пишут source('raw', 'customers'):

-- models/staging/stg_customers.sql
select
    id as customer_id,
    lower(email) as email
from {{ source('raw', 'customers') }}

Проверка свежести источника

Раз источник объявлен с loaded_at_field, dbt умеет проверять, не устарели ли данные:

# Проверить свежесть всех источников
dbt source freshness

Если последняя загрузка старше порога — dbt предупредит или упадёт с ошибкой. Это ловит проблему «труба загрузки сломалась, а мы строим витрины на вчерашних данных».

source() против ref()

ФункцияНа что ссылаетсяСтроит ли dbt
source()Сырьё, загруженное извнеНет, только читает
ref()Другую модель dbtДа, строит и упорядочивает

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

Источники — это «листья» в начале DAG: у них нет того, что dbt строит, но они дают графу точку входа. {{ source('raw', 'customers') }} компилируется в полное имя raw.customers, взятое из YAML. Зависимость модели от источника тоже попадает в граф, поэтому в документации видно, какие витрины завязаны на какой источник — это бесценно при анализе влияния (lineage).

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

  • Хардкодить сырую таблицу в FROM. Теряется lineage, проверка свежести и переносимость — используйте source().
  • Путать source() и ref(). Сырьё извне — source(); модель dbt — ref().
  • Строить витрины прямо на источниках. Лучше сперва обернуть источник в staging-модель (об этом — раздел про слои).

Итоги

  • source() ссылается на сырые внешние таблицы, объявленные в YAML; dbt их читает, но не строит.
  • Источники дают lineage, переносимость и проверку свежести (dbt source freshness).
  • Сырьё — source(), модели dbt — ref().
Проверьте себя
1. Чем source() отличается от ref()?
AНичем, это синонимы
Bsource() ссылается на сырые внешние таблицы, которые dbt не строит, а только читает
Csource() быстрее
Dsource() создаёт таблицы
2. Что даёт объявление источника с loaded_at_field?
AУскорение запросов
BВозможность проверять свежесть данных через dbt source freshness
CАвтоматическое создание таблицы
DШифрование данных