Learning Platform
Глоссарий Troubleshooting
Урок 05.04 · 17 мин
Начальный
targetcompiledrundbt-compiledbt-show

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 vs run
compiled/Чистый SELECT. Полезно для: понимания логики модели, копирования в ad-hoc SQL editor, проверки что Jinja развернулся правильно, изучения зависимостей.
run/DDL wrapper. Полезно для: понимания что warehouse реально увидел, debug incremental MERGE, проверки что create/replace работает.

Смотрите 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

Что делает:

  1. parse — найти все модели, sources, tests
  2. render Jinja — раскрыть {{ ref }}, {{ var }}, макросы
  3. Записать compiled SQL в target/compiled/
  4. Записать run SQL в target/run/ (но без выполнения в warehouse)
  5. Обновить 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>

Что делает:

  1. parse + render (как dbt compile)
  2. Выполнить compiled SELECT в warehouse
  3. Показать первые 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

КомандаParseRender JinjaExecute SELECTMaterializeВремя
dbt parseДаНетНетНетсекунды
dbt compileДаДаНетНетсекунды
dbt showДаДаДаНетсекунды-минута
dbt runДаДаДаДа (table/view/etc)минуты на большом проекте
dbt buildДаДаДаДаминуты + tests

Иерархия по влиянию:

  • dbt parse — самое лёгкое, только проверка проекта
  • dbt compile — добавляет рендеринг (нужно для debug)
  • dbt show — добавляет выполнение, но без побочных эффектов в warehouse
  • dbt 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) пересоздаст всё с нуля.

WARNING

dbt clean не удаляет таблицы из warehouse — только локальные артефакты. Если хотите очистить warehouse — это отдельная задача (например, удалить схему вручную или через post-hook).


Попробуй сам

Используйте проект с моделями из предыдущих уроков.

  1. Запустите dbt compile — это не должно тронуть DuckDB
  2. Откройте target/compiled/learning_models/models/marts/fct_orders.sql (если у вас есть эта модель — иначе любая mart-модель)
  3. Откройте target/run/learning_models/models/marts/fct_orders.sql — сравните: compiled vs run
  4. Запустите dbt show --select stg_orders --limit 3 — увидите 3 строки в терминале
  5. Запустите dbt show --inline "SELECT count(*) FROM {{ ref('stg_orders') }}" — увидите количество строк
  6. Откройте target/run_results.json после dbt build — увидите все executions с timings
  7. Запустите cat target/manifest.json | jq '.nodes | keys' — список всех нод проекта в формате model.<project>.<name>
  8. Запустите dbt clean — увидите удаление target/ и dbt_packages/

Хранение manifest: CI-артефакты
Проверка знанийKnowledge check
Вы изменили модель fct_orders, запустили 'dbt run --select fct_orders' и видите ошибку 'Catalog Error: Table dev.main.int_orders does not exist'. Как использовать target/compiled и target/run, чтобы диагностировать проблему?
ОтветAnswer
Сначала посмотрите target/compiled/fct_orders.sql — увидите compiled SELECT. Если в нём ссылка на int_orders, проверьте, что эта модель действительно есть в проекте — может быть typo в ref(). Если ref() корректен и int_orders есть в проекте, проблема в том, что вы запустили только fct_orders (не +fct_orders или int_orders отдельно), и upstream-модель int_orders физически не построена в warehouse. dbt не знал, что int_orders нужно перестроить раньше — потому что вы явно указали --select fct_orders без +. Решения: (1) запустить dbt run --select +fct_orders для пересборки всей цепочки upstream; (2) убедиться, что int_orders уже была построена ранее (dbt show --select int_orders проверит — если модель ещё не материализована, выдаст ту же ошибку). Это типичная ошибка — забыть про + при селекторе. Правильный pattern: при изменении mart-модели обычно --select +<mart>+ (включая upstream для гарантии актуальности и downstream для regression).

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 6. В чём отличие target/compiled/<model>.sql от target/run/<model>.sql?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4