target/ deep dive: compiled vs run
В предыдущих уроках мы видели, что dbt создаёт папку target/ с разными артефактами. В этом уроке — глубокое погружение в:
- Что такое compiled SQL и run SQL
- Команда
dbt compile(компиляция без выполнения) - Команда
dbt show(быстрый просмотр результата) - Манипуляции через manifest.json для понимания состояния проекта
После урока вы будете уметь дебажить любую dbt-модель через target/ без активного DuckDB.
Что лежит в target/
После dbt run в target/ находится:
target/
├── compiled/<project>/<path>/<model>.sql # SELECT после Jinja-рендеринга
├── run/<project>/<path>/<model>.sql # SELECT, обёрнутый в DDL
├── manifest.json # Сериализованный DAG
├── run_results.json # Результаты последнего запуска
├── catalog.json # Schema warehouse (от docs generate)
├── partial_parse.msgpack # Кэш парсинга для ускорения
├── graph.gpickle # Сериализованный граф (внутреннее)
└── semantic_manifest.json # Semantic Layer (если используется)
Главные для debug — compiled/ и run/. Поэтому копаем в них.
compiled/ — чистый SQL после Jinja
target/compiled/<project>/<path>/<model>.sql — это результат раскрытия Jinja:
{{ ref('X') }}заменено на"database"."schema"."X"{{ source('Y', 'Z') }}заменено на"db"."schema"."Z"{{ var('var_name') }}подставлено в значение- Все макросы развёрнуты в их SQL
- Циклы
{% for %}и условия{% if %}разрешены
Этот файл можно скопировать и выполнить в SQL editor warehouse — получите результат как у модели.
Пример. Исходная модель models/marts/orders_revenue.sql:
{{ config(materialized='table') }}
SELECT
customer_id,
{{ var('start_date_var', '2026-01-01') }} AS analysis_start,
SUM(amount) AS total_revenue
FROM {{ ref('stg_orders') }}
WHERE order_date >= '{{ var("start_date_var", "2026-01-01") }}'
GROUP BY 1
После dbt compile:
cat target/compiled/learning_models/models/marts/orders_revenue.sql
SELECT
customer_id,
'2026-01-01' AS analysis_start,
SUM(amount) AS total_revenue
FROM "dev"."main"."stg_orders"
WHERE order_date >= '2026-01-01'
GROUP BY 1
Что произошло:
{{ config(...) }}исчез (это не SQL, а metadata){{ ref('stg_orders') }}->"dev"."main"."stg_orders"{{ var('start_date_var', '2026-01-01') }}->'2026-01-01'(default взят)
Этот SQL можно прямо вставить в duckdb dev.duckdb и выполнить.
run/ — то же, обёрнутое в DDL
target/run/<project>/<path>/<model>.sql — это тот же compiled SQL, обёрнутый в DDL для материализации.
Для нашего orders_revenue (materialized=table):
create or replace table "dev"."main"."orders_revenue"
as (
SELECT
customer_id,
'2026-01-01' AS analysis_start,
SUM(amount) AS total_revenue
FROM "dev"."main"."stg_orders"
WHERE order_date >= '2026-01-01'
GROUP BY 1
);
Если бы materialization была view:
create or replace view "dev"."main"."orders_revenue"
as (
SELECT ...
);
Если incremental — там целая сложная MERGE/DELETE+INSERT логика. Подробнее в модуле 7.
run-SQL — это то, что dbt реально отправил в warehouse. В DuckDB, в Snowflake, в BigQuery — везде. Если хотите воспроизвести проблему — берёте run-SQL и выполняете в SQL editor.
compiled vs run: когда какой смотреть
Смотрите compiled когда:
- Хотите понять, что вы написали после раскрытия Jinja
- Копируете SQL в ad-hoc запрос для отладки
- Проверяете, что
ref()/source()/var()развернулись правильно - Изучаете макрос — что он развернул в SQL
- Анализируете performance запроса (compiled показывает, что оптимизатор увидит)
Смотрите run когда:
- Хотите понять, что warehouse реально увидел
- Debug-ите incremental модель (там сложная MERGE-логика, которую видно только в run)
- Проверяете, что DDL wrapper корректный
- Анализируете, почему
dbt runпадает на execute (run-SQL точно то, что упало)
В 80% дев-сценариев compiled достаточно. run пригождается для incremental.
dbt compile: компиляция без выполнения
Команда:
dbt compile
Что делает:
- parse — найти все модели, sources, tests
- render Jinja — раскрыть
{{ ref }},{{ var }}, макросы - Записать compiled SQL в
target/compiled/ - Записать run SQL в
target/run/(но без выполнения в warehouse) - Обновить
target/manifest.json
Что НЕ делает:
- Не подключается к warehouse
- Не создаёт таблицы/view
- Не выполняет тесты
Полезно для:
- Быстро проверить, что Jinja валидный, без долгого dbt run
- Получить compiled SQL для анализа без побочных эффектов
- В CI — этап «можно ли проект скомпилировать» перед actual run
Опции:
dbt compile --select +fct_orders # только эта модель и upstream
dbt compile --vars '{"start_date": "2026-06-01"}' # с переопределением vars
dbt compile --target prod # компиляция для prod target
Время выполнения — секунды (vs минуты для dbt run на большом проекте).
dbt show: быстрый просмотр результата
Команда:
dbt show --select <model> --limit <N>
Что делает:
- parse + render (как dbt compile)
- Выполнить compiled SELECT в warehouse
- Показать первые N строк в терминале
Пример:
dbt show --select stg_orders --limit 5
Вывод:
07:20:11 Running with dbt=1.10.3
07:20:11 Registered adapter: duckdb=1.10.1
07:20:11 Previewing node 'stg_orders':
| order_id | customer_id | amount | order_date |
| -------- | ----------- | ------ | ---------- |
| 100 | 1 | 50.00 | 2026-01-01 |
| 101 | 2 | 75.50 | 2026-01-15 |
| 102 | 1 | 120.00 | 2026-02-01 |
| 103 | 3 | 30.00 | 2026-02-05 |
Что важно:
dbt showне материализует модель в warehouse — просто выполняет SELECT- Если в DAG upstream-модели ещё не построены — будет ошибка (нужно
dbt run --select +<model>сначала) - limit по умолчанию — 5, можно задать
--limit 100
Удобно для:
- Sanity check после изменения модели
- Просмотр результата без открывания DuckDB CLI
- Демонстрация в чате/PR — копируешь markdown-таблицу из вывода
Альтернатива — --inline:
dbt show --inline "SELECT * FROM {{ ref('stg_orders') }} WHERE amount > 100"
Это позволяет выполнить произвольный SELECT с dbt-функциями (ref/source/var/macros) и увидеть результат. Полезно для ad-hoc проверок без создания файла.
dbt show vs dbt run vs dbt compile
| Команда | Parse | Render Jinja | Execute SELECT | Materialize | Время |
|---|---|---|---|---|---|
dbt parse | Да | Нет | Нет | Нет | секунды |
dbt compile | Да | Да | Нет | Нет | секунды |
dbt show | Да | Да | Да | Нет | секунды-минута |
dbt run | Да | Да | Да | Да (table/view/etc) | минуты на большом проекте |
dbt build | Да | Да | Да | Да | минуты + tests |
Иерархия по влиянию:
dbt parse— самое лёгкое, только проверка проектаdbt compile— добавляет рендеринг (нужно для debug)dbt show— добавляет выполнение, но без побочных эффектов в warehousedbt run— реальная материализацияdbt build— run + tests + seeds + snapshots в правильном порядке
В дев-цикле обычно: меняешь модель -> dbt run --select <model>+ -> dbt test --select <model>+ или сразу dbt build --select <model>+.
Debug workflow через target/
Сценарий: модель fct_orders показывает странные данные. Как дебажить?
Step 1: смотрю compiled SQL
cat target/compiled/learning_models/models/marts/fct_orders.sql
Получаю чистый SELECT. Проверяю — все ли ref правильные, нет ли typo в условиях.
Step 2: копирую compiled SQL в DuckDB CLI
duckdb dev.duckdb
-- Вставляю compiled SELECT
SELECT
o.order_id,
c.first_name,
...
FROM "dev"."main"."stg_orders" o
LEFT JOIN "dev"."main"."stg_customers" c USING (customer_id)
WHERE order_date >= '2026-01-01';
Выполняю -> смотрю результат -> нахожу проблему (например, LEFT JOIN потерял строки, потому что customer_id NULL).
Step 3: исправляю исходную модель, делаю dbt run --select fct_orders, проверяю.
Это и есть «debug workflow через target/». Никаких guess-and-check, никакой mistery — у вас точный SQL, который вы можете воспроизвести.
manifest.json: программный доступ к DAG
target/manifest.json — большой JSON. Полезно знать структуру для скриптов.
Топ-уровень:
{
"metadata": {...},
"nodes": {...}, // модели + tests + snapshots + seeds
"sources": {...},
"macros": {...},
"exposures": {...},
"metrics": {...},
"groups": {...},
"parent_map": {...},
"child_map": {...},
"selectors": {...}
}
Полезные запросы через jq:
# Список всех моделей
cat target/manifest.json | jq '.nodes | to_entries[] | select(.value.resource_type == "model") | .key'
# Зависимости конкретной модели
cat target/manifest.json | jq '.nodes."model.learning_models.fct_orders".depends_on'
# Все модели в подпапке staging
cat target/manifest.json | jq '.nodes | to_entries[] | select(.value.path | startswith("staging/")) | .value.name'
# Compiled код модели
cat target/manifest.json | jq -r '.nodes."model.learning_models.fct_orders".compiled_code'
Эти команды — основа для dbt artifacts package (загружает manifest в warehouse) и Elementary (observability).
partial_parse.msgpack: кэш парсинга
target/partial_parse.msgpack — сериализованный кэш парсинга проекта в формате msgpack.
При следующем dbt run dbt загружает кэш и парсит только изменённые файлы, что ускоряет старт на больших проектах (1000+ моделей) с секунд до милисекунд.
Если кэш «протух» (несовместимая версия dbt) — dbt сам его инвалидирует. Можно вручную:
dbt clean # Удаляет target/ + dbt_packages/
# или
rm -f target/partial_parse.msgpack
Это не критично — следующий dbt parse его пересоздаст.
catalog.json: warehouse schema
target/catalog.json создаётся командой dbt docs generate. Это информация из information_schema warehouse:
- Какие колонки в таблице/view
- Типы колонок
- Размеры (для warehouses, которые их выдают)
- Statistics (для warehouses со статистикой)
Это снимок состояния warehouse в момент dbt docs generate, не из dbt-моделей. Используется в dbt docs serve для богатого UI.
Если catalog.json нет — dbt docs serve показывает только структуру моделей из manifest, без warehouse-stats.
run_results.json: лог последнего запуска
target/run_results.json создаётся после каждого dbt run / dbt test / dbt build.
Внутри — для каждой ноды:
{
"unique_id": "model.learning_models.fct_orders",
"status": "success",
"execution_time": 0.052,
"message": "OK",
"thread_id": "Thread-1",
"timing": [
{"name": "compile", "started_at": "...", "completed_at": "..."},
{"name": "execute", "started_at": "...", "completed_at": "..."}
]
}
Status может быть: success, error, skipped, pass, fail, warn.
Используется для:
- CI-скрипты — парсят и шлют alerts в Slack при error
- Observability — dbt artifacts package импортирует это в warehouse
- Debug —
cat run_results.json | jq '.results[] | select(.status == "error")'показывает все ошибочные ноды
dbt clean: чистка артефактов
dbt clean
Удаляет всё, что задано в clean-targets: в dbt_project.yml. Стандартно — target/ и dbt_packages/.
Когда полезно:
- Перед commit (чтобы быть уверенным, что не закоммитили лишнее)
- При проблемах с partial parse cache
- В CI — start with clean state
После dbt clean следующая команда (dbt run / dbt deps) пересоздаст всё с нуля.
dbt clean не удаляет таблицы из warehouse — только локальные артефакты. Если хотите очистить warehouse — это отдельная задача (например, удалить схему вручную или через post-hook).
Попробуй сам
Используйте проект с моделями из предыдущих уроков.
- Запустите
dbt compile— это не должно тронуть DuckDB - Откройте
target/compiled/learning_models/models/marts/fct_orders.sql(если у вас есть эта модель — иначе любая mart-модель) - Откройте
target/run/learning_models/models/marts/fct_orders.sql— сравните: compiled vs run - Запустите
dbt show --select stg_orders --limit 3— увидите 3 строки в терминале - Запустите
dbt show --inline "SELECT count(*) FROM {{ ref('stg_orders') }}"— увидите количество строк - Откройте
target/run_results.jsonпослеdbt build— увидите все executions с timings - Запустите
cat target/manifest.json | jq '.nodes | keys'— список всех нод проекта в форматеmodel.<project>.<name> - Запустите
dbt clean— увидите удаление target/ и dbt_packages/
Хранение manifest: CI-артефакты