dbt предоставляет три команды специально для дебага. Каждая отвечает на свой вопрос:
dbt debug— «у меня вообще запускается?» (соединение, профиль, конфиг)dbt compile— «какой SQL dbt сгенерирует?» (без запуска в БД)dbt show— «что эта модель сейчас бы вернула?» (запуск превью, без материализации)
Из этой триады junior начинают использовать compile после первой реальной ошибки, debug — после первой проблемы с подключением, show — после третьего «а что в этой модели сейчас?».
Когда что-то не работает, идёшь сверху вниз: сначала проверь соединение (debug), потом SQL (compile), потом данные (show).
dbt debug — диагностика окружения
Запускают первым делом после pip install, после git clone, после смены ветки, после правки profiles.yml. Команда проверяет:
- dbt-core установлен и какой версии
- python-окружение
dbt_project.ymlнайден и валиденprofiles.ymlнайден и валиден- Профиль из
dbt_project.ymlсуществует вprofiles.yml - Адаптер установлен (например,
dbt-duckdb) - Соединение к warehouse работает
$ dbt debug
14:23:15 Running with dbt=1.10.2
14:23:15 dbt version: 1.10.2
14:23:15 python version: 3.11.7
14:23:15 python path: /Users/me/.venv/bin/python
14:23:15 os info: macOS-14.5
14:23:15 Using profiles dir at /Users/me/.dbt
14:23:15 Using profiles.yml file at /Users/me/.dbt/profiles.yml
14:23:15 Using dbt_project.yml file at /Users/me/jaffle_shop/dbt_project.yml
14:23:15 Configuration:
14:23:15 profiles.yml file [OK found and valid]
14:23:15 dbt_project.yml file [OK found and valid]
14:23:15 Required dependencies:
14:23:15 - git [OK found]
14:23:15 Connection:
14:23:15 database: jaffle_shop
14:23:15 schema: main
14:23:15 path: ./jaffle_shop.duckdb
14:23:15 Connection test: [OK connection ok]
14:23:15 All checks passed!
Каждый блок — независимая проверка. Если есть [ERROR ...] — фикс этого блока перед движением дальше.
Типичные провалы
profiles.yml file [ERROR not found]
dbt ищет profiles.yml в ~/.dbt/ или в каталоге, указанном через DBT_PROFILES_DIR env var, или через флаг --profiles-dir. Если файла нет — создай:
mkdir -p ~/.dbt
nano ~/.dbt/profiles.yml
И заполни шаблоном для своего адаптера (см. модуль 3-setup).
Profile not found: jaffle_shop
В dbt_project.yml стоит profile: jaffle_shop, но в profiles.yml такого ключа верхнего уровня нет. Проверь, что они совпадают по имени.
Connection test: [ERROR ...]
База недоступна. Для DuckDB причины:
- неверный
path(относительный путь от текущей директории, не от проекта) - файл
.duckdbповреждён (попробуй удалить и создать заново) - DuckDB занят другим процессом (single-writer per file)
Для облачных warehouses:
- неверные креды (
env_varуказывает на пустую переменную) - VPN/IP whitelist
- неправильный регион/host
dbt debug --connection — пропустить проверки файлов, тестировать только соединение. Полезно когда уже знаешь, что конфиг валидный, и хочешь быстро проверить «база живая?».
dbt debug --config-dir — печатает путь к profiles.yml. Полезно когда есть несколько venv с разными конфигами.
Завести алиас в shell: alias dd='dbt debug --connection'. Каждый раз перед началом работы — dd. 5 секунд экономят часы.
dbt compile — финальный SQL без запуска
dbt compile развернёт Jinja и положит финальный SQL в target/compiled/<project>/.../<model>.sql. В warehouse при этом не обращается — это чисто текстовая трансформация.
$ dbt compile --select customers
14:30:11 Running with dbt=1.10.2
14:30:11 Found 8 models, 14 tests, ...
14:30:11 Concurrency: 4 threads
14:30:11
14:30:11 1 of 1 OK compiled customers ... [OK in 0.02s]
14:30:11 Finished compiling 1 model in 0 hours 0 minutes and 0.45 seconds
После этого в target/compiled/jaffle_shop/models/marts/customers.sql лежит готовый SQL. Открой в редакторе:
-- было в models/marts/customers.sql:
-- select c.customer_id, count(o.order_id) as order_count
-- from {{ ref('stg_jaffle__customers') }} c
-- left join {{ ref('stg_jaffle__orders') }} o on c.customer_id = o.customer_id
-- group by c.customer_id
-- стало в target/compiled/...:
select c.customer_id, count(o.order_id) as order_count
from "jaffle_shop"."main"."stg_jaffle__customers" c
left join "jaffle_shop"."main"."stg_jaffle__orders" o on c.customer_id = o.customer_id
group by c.customer_id
{{ ref('stg_jaffle__customers') }} развернулось в "jaffle_shop"."main"."stg_jaffle__customers" — фактическое имя таблицы, которое dbt отправит в БД.
Зачем это нужно
-
Дебаг ошибок SQL. Скопируй SQL в DuckDB shell, прогоны вручную, увидь точную ошибку с line:column.
-
Понять, что Jinja развернулось правильно. Если у тебя есть условие
{% if target.name == 'prod' %}— посмотри, что вtarget.nameсейчас, какая ветка выбралась. -
Code review без запуска dbt. В PR можно приложить
target/compiled/и review-er увидит финальный SQL без локального запуска. -
Производительность. Скопировать SQL в
EXPLAIN ANALYZEи посмотреть план запроса в warehouse.
dbt compile --select +my_model — скомпилирует модель и весь её upstream. dbt compile без флагов — всё в проекте, что бывает удобно для статического анализа.
dbt compile --inline
Можно скомпилировать произвольную Jinja-строку, не привязанную к файлу:
$ dbt compile --inline "SELECT count(*) FROM {{ ref('customers') }}"
14:32:15 Compiled inline node
14:32:15
14:32:15 SELECT count(*) FROM "jaffle_shop"."main"."customers"
Полезно для быстрых проверок: «как именно ref() этой модели развернётся?»
dbt show — превью без материализации
dbt show отличается от compile тем, что он выполняет SQL в warehouse — но не материализует таблицу, а просто выбирает первые N строк и печатает в терминал:
$ dbt show --select customers --limit 5
14:35:01 Previewing node 'customers':
| customer_id | first_name | last_name | order_count |
|-------------|------------|-----------|-------------|
| 1 | Anna | Petrova | 3 |
| 2 | Boris | Ivanov | 1 |
| 3 | Carla | Smith | 5 |
| 4 | Dmitry | Volkov | 2 |
| 5 | Elena | Sidorova | 0 |
Внутри dbt оборачивает скомпилированный SQL в SELECT * FROM (...) LIMIT 5 и шлёт в warehouse. Никаких CREATE TABLE / CREATE VIEW — материализации не происходит.
--limit N — кол-во строк. По умолчанию 5.
Зачем
- Быстро проверить, что модель вообще что-то возвращает, не пересоздавая таблицу.
- Локально итерировать на staging-моделях: пишешь SQL,
dbt show— видишь результат. Не нужно полноеdbt run, который для view ещё ладно, для table — дорого. - Дебаг с конкретными данными: модель возвращает «не то» —
showпокажет, что именно.
dbt show --inline
То же, что compile —inline, но с выполнением:
$ dbt show --inline "SELECT count(*) FROM {{ ref('customers') }}"
14:37:02 Previewing inline node:
| count_star() |
|--------------|
| 1000 |
Удобный способ быстро проверить, сколько строк сейчас в модели, не открывая SQL-клиент.
Caveats
dbt showне подходит для тяжёлых моделей — он всё-таки выполняет SQL. Если модель с 10 джойнами и группировкой по 1B строк, превью займёт минуты. В таких случаях надёжнееdbt compile+ ручнойEXPLAIN.- Результат печатается в текстовый формат — для серьёзного анализа лучше открыть BI или SQL-клиент.
Идеальный workflow дебага
Сценарий: ты получил ошибку в dbt run:
Error in model orders (models/marts/orders.sql)
Database Error in model orders
Catalog Error: Table "stg_jaffle__orders" does not exist!
Did you mean "main.stg_jaffle__orders"?
Шаг 1: dbt debug
Проверь, что окружение в порядке:
$ dbt debug --connection
14:40:01 Connection test: [OK]
14:40:01 All checks passed!
OK, не в этом дело. Идём дальше.
Шаг 2: dbt compile
Скомпилируй проблемную модель и посмотри финальный SQL:
$ dbt compile --select orders
14:40:15 Compiled orders ...
Открой target/compiled/jaffle_shop/models/marts/orders.sql:
select o.*, c.customer_name
from stg_jaffle__orders o -- БАГ! Должно быть {{ ref('stg_jaffle__orders') }}
left join {{ ref('stg_jaffle__customers') }} c on o.customer_id = c.customer_id
— ага, кто-то написал прямое имя таблицы вместо ref(). Это и есть «hardcoded table name» антипаттерн из модуля 4. Меняем:
from {{ ref('stg_jaffle__orders') }} o
Шаг 3: dbt show
После фикса прогоним превью:
$ dbt show --select orders --limit 5
14:42:08 Previewing node 'orders':
| order_id | customer_id | customer_name | total_cents |
|----------|-------------|---------------|-------------|
| 1 | 1 | Anna Petrova | 2400 |
...
Работает. Теперь dbt run --select orders для реальной материализации.
Этот цикл debug -> compile -> show -> run — главный mental loop junior в dbt.
Когда dbt compile не помогает
Иногда баг не в SQL, а в Jinja-логике, которая ещё не дошла до компиляции. Тогда dbt compile падает раньше, чем что-то напишет в target/.
Типичные случаи:
- Macro определён, но
dbt depsне запускали — пакет физически не на диске. - Опечатка в имени макроса:
{{ dbt_utils.starr(...) }}(двеr) — Jinja не может найти. {% if execute %}вокругrun_query— если попадаешь на parse phase, query не запускается; об этом — модуль 10.- Конфиг-блок в неправильном месте (после
select— игнорируется).
В этих случаях смотри logs/dbt.log — об этом следующий урок.
Попробуй сам
-
Сломай профиль: переименуй
profiles.yml -> profiles.yml.bak. Запустиdbt debug. Прочитай ошибку. Восстанови. -
В модели
customers.sqlнамеренно опечатайся в имени макроса: вместо{{ ref('stg_jaffle__customers') }}напиши{{ rref('stg_jaffle__customers') }}. Запустиdbt compile --select customers. Прочитай ошибку. Исправь. -
Запусти
dbt show --inline "SELECT now(), {{ target.name }}, {{ target.schema }}". Посмотри, что развернулось. -
Запусти
dbt show --select stg_jaffle__orders --limit 3. Сравни вывод сdbt run --select stg_jaffle__orders— где материализация и где её нет.
Чек-лист
dbt debug— первый шаг при любой проблеме. Проверяет окружение и соединение.dbt debug --connection— быстрый тест только подключения.dbt compile --select <model>— финальный SQL вtarget/compiled/. Открой и копируй в SQL-клиент.dbt compile --inline "..."— компиляция произвольной строки.dbt show --select <model> --limit N— превью результата без материализации.dbt show --inline "..."— выполнение произвольной строки и вывод результата.- Идеальный workflow:
debug->compile->show->run. Не пропускай шаги. - Если
compileсам падает — смотриlogs/dbt.log(следующий урок).