Стадия: Подготовка наших атомарных строительных блоков
Стадия — это место, где начинается наше путешествие. Это основа нашего проекта, где мы собираем все отдельные компоненты, которые будем использовать для создания более сложных и полезных моделей в проекте.
Мы будем использовать аналогию для работы с dbt на протяжении этого руководства: думать модульно в терминах атомов, молекул и более сложных выходов, таких как белки или клетки (мы заранее извиняемся перед химиками или биологами за наше неизбежное растягивание этой метафоры). В рамках этой концепции, если данные нашей исходной системы — это суп из сырой энергии и кварков, то вы можете думать о стадии как о конденсации и очистке этого материала в отдельные атомы, с которыми мы позже построим более сложные и полезные структуры.
Стадия: Файлы и папки
Давайте увеличим масштаб каталога стадии из нашего дерева файлов models
в обзоре и пройдемся по тому, что здесь происходит.
models/staging
├── jaffle_shop
│ ├── _jaffle_shop__docs.md
│ ├── _jaffle_shop__models.yml
│ ├── _jaffle_shop__sources.yml
│ ├── base
│ │ ├── base_jaffle_shop__customers.sql
│ │ └── base_jaffle_shop__deleted_customers.sql
│ ├── stg_jaffle_shop__customers.sql
│ └── stg_jaffle_shop__orders.sql
└── stripe
├── _stripe__models.yml
├── _stripe__sources.yml
└── stg_stripe__payments.sql
- Папки. Структура папок чрезвычайно важна в dbt. Нам нужна последовательная структура не только для того, чтобы ориентироваться в кодовой базе, как в любом программном проекте, но и потому, что наша структура папок является одним из ключевых интерфейсов для понимания графа знаний, закодированного в нашем проекте (наряду с DAG и данными, выводимыми в наш склад). Она должна отражать, как данные текут, шаг за шагом, от множества моделей, соответствующих источникам, к меньшему количеству более богатых моделей, соответствующих бизнесу. Более того, мы можем использовать нашу структуру папок как средство выбора в dbt синтаксис выбора. Например, с вышеуказанной структурой, если мы загрузили свежие данные Stripe и хотим запустить все модели, которые строятся на наших данных Stripe, мы можем легко запустить
dbt build --select staging.stripe+
и мы готовы к созданию более актуальных отчетов о платежах.- ✅ Подкаталоги на основе системы источника. Наша внутренняя транзакционная база данных — это одна система, данные, которые мы получаем из API Stripe — это другая, и, наконец, события из нашего инструментария Snowplow. Мы обнаружили, что это лучшее группирование для большинства компаний, так как системы источников, как правило, имеют схожие методы загрузки и свойства между таблицами, и это позволяет нам легко работать с этими схожими наборами.
- ❌ Подкаталоги на основе загрузчика. Некоторые люди пытаются группировать по способу загрузки данных (Fivetran, Stitch, пользовательские синхронизации), но это слишком широко, чтобы быть полезным в проекте любого реального размера.
- ❌ Подкаталоги на основе бизнес-группировки. Еще один подход, который мы не рекомендуем, — это разделение по бизнес-группировкам на стадии и создание подкаталогов, таких как 'маркетинг', 'финансы' и т.д. Ключевая цель любого отличного проекта dbt должна заключаться в установлении единого источника истины. Разбивая вещи слишком рано, мы открываем возможность создания пересечений и конфликтующих определений (например, маркетинг и финансы имеют разные фундаментальные таблицы для заказов). Мы хотим, чтобы все строили с одним и тем же набором атомов, поэтому, по нашему опыту, начало наших преобразований с нашей структурой стадии, отражающей структуры системы источника, является лучшим уровнем группировки для этого шага.
- Имена файлов. Создание последовательного шаблона именования файлов критически важно в dbt. Имена файлов должны быть уникальными и соответствовать имени модели при выборе и создании в хранилище. Мы рекомендуем включать как можно больше ясной информации в имя файла, включая префикс для уровня, на котором находится модель, важную информацию о группировке и конкретную информацию об объекте или преобразовании в модели.
- ✅
stg_[source]__[entity]s.sql
- двойное подчеркивание между системой источника и объектом помогает визуально различать отдельные части в случае, если имя источника состоит из нескольких слов. Например,google_analytics__campaigns
всегда понятно, тогда как для кого-то незнакомогоgoogle_analytics_campaigns
может бытьanalytics_campaigns
из системы источникаgoogle
так же легко, как иcampaigns
из системы источникаgoogle_analytics
. Думайте об этом как об оксфордской запятой, дополнительная ясность стоит дополнительных знаков препинания. - ❌
stg_[entity].sql
- может быть достаточно специфичным вначале, но со временем станет проблемой. Добавление системы источника в имя файла помогает в обнаружении и позволяет понять, откуда пришла компонентная модель, даже если вы не смотрите на дерево файлов. - ✅ Множественное число. SQL, и особенно SQL в dbt, должен читаться как можно больше как проза. Мы хотим использовать широкую ясность и декларативную природу SQL, когда это возможно. Таким образом, если в вашей таблице
orders
нет единственного заказа, множественное число — это правильный способ описания того, что находится в таблице с несколькими строками.
- ✅
Стадия: Модели
Теперь, когда мы поняли, как файлы и папки сочетаются друг с другом, давайте заглянем внутрь одного из этих файлов и разберемся, что делает модель стадии хорошо структурированной.
Ниже приведен пример стандартной модели стадии (из нашей модели stg_stripe__payments
), которая иллюстрирует общие шаб лоны в слое стадии. Мы организовали нашу модель в два CTE: один для извлечения таблицы источника через макрос источника и другой для применения наших преобразований.
Хотя наши последующие слои преобразований будут значительно различаться от модели к модели, каждая из наших моделей стадии будет следовать этому точному шаблону. Таким образом, мы должны убедиться, что установленный нами шаблон является надежным и последовательным.
-- stg_stripe__payments.sql
with
source as (
select * from {{ source('stripe','payment') }}
),
renamed as (
select
-- ids
id as payment_id,
orderid as order_id,
-- strings
paymentmethod as payment_method,
case
when payment_method in ('stripe', 'paypal', 'credit_card', 'gift_card') then 'credit'
else 'cash'
end as payment_type,
status,
-- numerics
amount as amount_cents,
amount / 100.0 as amount,
-- booleans
case
when status = 'successful' then true
else false
end as is_completed_payment,
-- dates
date_trunc('day', created) as created_date,
-- timestamps
created::timestamp_ltz as created_at
from source
)
select * from renamed
-
Основываясь на вышеизложенном, наиболее стандартные типы преобразований моделей стадии:
- ✅ Переименование
- ✅ Приведение типов
- ✅ Базовые вычисления (например, центы в доллары)
- ✅ Категоризация (использование условной логики для группировки значений в категории или булевы значения, как в операторах
case when
выше) - ❌ Соединения — цель моделей стадии — очистить и подготовить отдельные концепции, соответствующие источнику, для дальнейшего использования. Мы создаем наиболее полезную версию таблицы системы источ ника, которую мы можем использовать как новый модульный компонент для нашего проекта. По нашему опыту, соединения почти всегда плохая идея здесь — они создают немедленное дублирование вычислений и запутанные отношения, которые распространяются вниз по потоку — хотя иногда бывают исключения (обратитесь к базовым моделям для получения дополнительной информации).
- ❌ Агрегации — агрегации подразумевают группировку, и мы не делаем этого на этом этапе. Помните — модели стадии — это ваше место для создания строительных блоков, которые вы будете использовать на протяжении всего остального проекта — если мы начнем изменять зернистость наших таблиц, группируя на этом слое, мы потеряем доступ к исходным данным, которые нам, вероятно, понадобятся в какой-то момент. Мы просто хотим, чтобы наши отдельные концепции были очищены и готовы к использованию, и будем обрабатывать агрегацию значений дальше по потоку.
-
✅ Материализованы как представления. Посмотрев на частичное представление нашего
dbt_project.yml
ниже, мы можем увиде ть, что мы настроили весь каталог стадии для материализации как представления. Поскольку они не предназначены для того, чтобы быть конечными артефактами сами по себе, а скорее строительными блоками для последующих моделей, модели стадии обычно должны быть материализованы как представления по двум ключевым причинам:-
Любая последующая модель (обсуждается подробнее в marts), ссылающаяся на наши модели стадии, всегда будет получать самые свежие данные из всех компонентных представлений, которые она объединяет и материализует
-
Это позволяет избежать траты места в хранилище на модели, которые не предназначены для запросов потребителями данных, и, следовательно, не нуждаются в быстром или эффективном выполнении
# dbt_project.yml
models:
jaffle_shop:
staging:
+materialized: view
-
-
Модели стадии — это единственное место, где мы будем использовать
source
макрос, и наши модели стадии должны иметь отношение 1-к-1 к нашим таблицам источников. Это означает, что для каждой таблицы системы источника у нас будет одна модель стадии, ссылающаяся на нее, действующая как ее точка входа — стадия — для использования дальше по потоку.
Модели стадии помогают нам держать наш код DRY. Модульная, повторно используемая структура dbt означает, что мы можем и должны продвигать любые преобразования, которые мы всегда будем хотеть использовать для данной компонентной модели, как можно дальше вверх по потоку. Это спасает нас от потенциальной траты кода, сложности и вычислений, выполняя одно и то же преобразование более одного раза. Например, если мы знаем, что всегда хотим, чтобы наши денежные значения были в виде чисел с плавающей запятой в долларах, но система источника — это целые числа и центы, мы хотим выполнить деление и приведение типов как можно раньше, чтобы мы могли ссылаться на это, а не повторять это снова и снова дальше по потоку.
Это приятное изменение для многих из нас, кто привык применять одни и те же наборы SQL-преобразований во многих местах из-за необходимости! Для нас, самая ранняя точка для этих 'всегда-желаемых' преобразований — это слой стадии, начальная точка входа в наш процесс преобразования. Принцип DRY в конечном итоге является лакмусовой бумажкой для того, должны ли преобразования происходить на слое стадии. Если мы захотим их в каждой последующей модели и они помогут нам устранить повторяющийся код, они, вероятно, в порядке.
Стадия: Другие соображения
-
Базовые модели, когда соединения необходимы для стадии концепций. Иногда, чтобы поддерживать чистый и DRY слой стадии, нам действительно нужно реализовать некоторые соединения, чтобы создать надежную концепцию для наших строительных блоков. В этих случаях мы рекомендуем создать подкаталог в каталоге стадии для рассматриваемой системы источника и строить
base
модели. Эти модели имеют все те же свойства, которые обычно находятся в слое стадии, они будут напрямую использовать сырые данные и выполнять не-соединительные преобразования, затем в моделях стадии мы будем соединять необходимые базовые модели. Наиболее распространенные случаи использования для создания базового слоя под папкой стадии:-
✅ Соединение отдельных таблиц удаления. Иногда система источника может хранить удаления в отдельной таблице. Обычно мы хотим убедиться, что можем пометить или отфильтровать удаленные записи для всех наших компонентных моделей, поэтому нам нужно будет соединить эти записи удаления с любыми из наших объектов, которые следуют этому шаблону. Это пример, показанный ниже для иллюстрации.
-- base_jaffle_shop__customers.sql
with
source as (
select * from {{ source('jaffle_shop','customers') }}
),
customers as (
select
id as customer_id,
first_name,
last_name
from source
)
select * from customers-- base_jaffle_shop__deleted_customers.sql
with
source as (
select * from {{ source('jaffle_shop','customer_deletes') }}
),
deleted_customers as (
select
id as customer_id,
deleted as deleted_at
from source
)
select * from deleted_customers-- stg_jaffle_shop__customers.sql
with
customers as (
select * from {{ ref('base_jaffle_shop__customers') }}
),
deleted_customers as (
select * from {{ ref('base_jaffle_shop__deleted_customers') }}
),
join_and_mark_deleted_customers as (
select
customers.*,
case
when deleted_customers.deleted_at is not null then true
else false
end as is_deleted
from customers
left join deleted_customers on customers.customer_id = deleted_customers.customer_id
)
select * from join_and_mark_deleted_customers -
✅ Объединение разрозненных, но симметричных источников. Типичный пример здесь — если вы управляете несколькими платформами электронной коммерции в различных территориях через SaaS-платформу, такую как Shopify. У вас будут совершенно идентичные схемы, но все загружены отдельно в ваше хранилище. В этом случае легче рассуждать о наших заказах, если все наши магазины объединены вместе, поэтому мы хотели бы обработать объединение в базовой модели, прежде чем продолжить наши обычные преобразования модели стадии на (теперь полном) наборе — вы можете углубиться в более подробную информацию об этом случае использования здесь.
-
-
Codegen для автоматизации генерации таблиц стадии. Очень полезно научиться писать модели стадии вручную, они просты и многочисленны, поэтому могут быть отличным способом усвоить стиль написания SQL в dbt. Также, мы неизбежно найдем себя в необходимости добавления специальных элементов в конкретные модели время от времени — например, в одной из ситуаций выше, требующих базовых моделей — поэтому полезно глубоко понять, как они работают. Однако, как только это понимание установлено, поскольку модели стадии в значительной степени строятся, следуя одним и тем же шаблонам, и должны быть построены 1-к-1 для каждой таблицы источника в системе источника, предпочтительно начать автоматизировать их создание. Для этого у нас есть пакет codegen. Это позволит вам автоматически генерировать весь исходный YAML и шаблоны моделей стадии, чтобы ускорить этот шаг, и мы рекомендуем использовать его в каждом проекте.
-
Каталог утилит. Хотя это не в папке
staging
, полезно рассматривать это как часть наших фундаментальных строительных блоков. Каталогmodels/utilities
— это место, где мы можем хранить любые модели общего назначения, которые мы генерируем из макросов или на основе семян, которые предоставляют инструменты, помогающие нам выполнять наше моделирование, а не данные для моделирования. Наиболее распространенный случай использования — это хребет дат, сгенерированный с помощью пакета dbt utils.
Это руководство следует порядку DAG, чтобы мы могли получить целостное представление о том, как эти три основных слоя строятся друг на друге для создания значимых продуктов данных. Важно отметить, что разработка моделей обычно не движется линейно через DAG. Чаще всего, мы должны начать с моделирования дизайна в электронной таблице, чтобы убедиться, что мы согласованы с нашими заинтересованными сторонами по целям вывода. Затем мы напишем SQL для генерации этого вывода и определим, какие таблицы задействованы. Как только у нас есть наша логика и зависимости, мы убедимся, что мы подготовили все необходимые атомарные части в проект, затем объединим их на основе логики, которую мы написали, чтобы создать наш mart. Наконец, с функционирующей моделью, работающей в dbt, мы можем начать рефакторинг и оптимизацию этого mart. Разделяя логику и перемещая части обратно вверх по потоку в промежуточные модели, мы обеспечиваем, что все наши модели чисты и читаемы, история нашего DAG ясна, и у нас больше возможностей для применения тщательного тестирования.