# dbt у 2026 році: трансформація даних, тестування та питання для співбесід > Повний гайд з dbt для дата-інженерів: пошарова модель даних, матеріалізації, стратегії тестування якості та реальні питання для технічних співбесід 2026 року. - Published: 2026-05-25 - Updated: 2026-05-25 - Author: SharpSkill - Tags: dbt, data-engineering, data-transformation, testing, interview, analytics-engineering, sql - Reading time: 10 min --- dbt (data build tool) став стандартним фреймворком для трансформації даних у сучасних аналітичних сховищах. Станом на 2026 рік понад 40 000 компаній використовують dbt у продакшн-середовищах для побудови надійних data-пайплайнів. Цей гайд охоплює ключові механіки dbt-трансформацій, стратегії тестування якості даних та питання, які найчастіше зустрічаються на технічних співбесідах з data engineering. > **Що саме робить dbt** > > dbt відповідає за T (Transform) у підході ELT. Він компілює SQL SELECT-запити у DDL-інструкції (CREATE TABLE, CREATE VIEW, MERGE) та виконує їх безпосередньо у сховищі даних — Snowflake, BigQuery, Redshift або Databricks. Контроль версій, розв'язання залежностей, тестування та документація входять до базової функціональності. ## Пошарова модель: Staging, Intermediate та Marts Добре структурований dbt-проєкт розділяє трансформації на три рівні. Такий підхід, популяризований [dbt-спільнотою](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview), забезпечує принцип єдиної відповідальності для кожної моделі та значно спрощує діагностику проблем. **Staging**-моделі розташовані найближче до сирих даних. Їхня функція чітко обмежена: перейменування колонок, приведення типів та фільтрація некоректних записів. Жодних JOIN-операцій та агрегацій на цьому рівні. ```sql -- models/staging/stg_orders.sql WITH source AS ( SELECT * FROM {{ source('ecommerce', 'raw_orders') }} ) SELECT id AS order_id, customer_id, CAST(order_date AS DATE) AS order_date, CAST(amount AS DECIMAL(10, 2)) AS order_amount, LOWER(status) AS order_status FROM source WHERE id IS NOT NULL ``` **Intermediate**-моделі застосовують бізнес-логіку — JOIN-операції, агрегації, віконні функції. Вони посилаються на staging-моделі через функцію `ref()`, яка реєструє залежності у DAG. ```sql -- models/intermediate/int_customer_orders.sql WITH orders AS ( SELECT * FROM {{ ref('stg_orders') }} ), customers AS ( SELECT * FROM {{ ref('stg_customers') }} ) SELECT c.customer_id, c.customer_name, COUNT(o.order_id) AS total_orders, SUM(o.order_amount) AS lifetime_value, MIN(o.order_date) AS first_order_date, MAX(o.order_date) AS last_order_date FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.customer_id, c.customer_name ``` **Marts** — це фінальний рівень споживання: чисті, задокументовані таблиці, до яких безпосередньо звертаються дашборди та аналітики. Пошарова архітектура гарантує, що збій у джерелі даних вплине лише на staging-рівень, а не на весь пайплайн. Кожен рівень може тестуватися незалежно від інших. ## Функція ref() та побудова DAG `ref()` — це не просто аліас для імені таблиці. Виклик `{{ ref('stg_orders') }}` виконує дві операції одночасно: розв'язує повне кваліфіковане ім'я таблиці на етапі компіляції та реєструє ребро залежності у направленому ациклічному графі (DAG) dbt. Без `ref()` dbt не має можливості визначити порядок виконання моделей. Поширена помилка — хардкодинг імен таблиць замість використання `ref()`. Це порушує відстеження залежностей і може призвести до виконання моделей раніше, ніж їхні upstream-залежності будуть готові. ```sql -- Bad: hardcoded reference, no dependency tracking SELECT * FROM analytics.stg_orders -- Good: ref() registers the dependency SELECT * FROM {{ ref('stg_orders') }} ``` DAG також забезпечує роботу таких команд, як `dbt run --select stg_orders+`, яка запускає модель та все, що від неї залежить нижче по графу. Це корисно для цільових перебудов після зміни схеми джерела даних. ## Матеріалізації: вибір стратегії зберігання dbt пропонує чотири вбудовані [матеріалізації](https://docs.getdbt.com/docs/build/materializations), кожна з яких підходить для різних патернів доступу до даних та обсягів. | Materialization | Storage | Best for | Trade-off | |----------------|---------|----------|----------| | `view` | No storage (computed on read) | Lightweight transforms, small datasets | Slow reads on large data | | `table` | Full table rebuild each run | Mart-layer models, fast reads | Rebuilds everything, higher cost | | `incremental` | Appends/merges new rows only | Large fact tables, event streams | More complex logic, needs `unique_key` | | `ephemeral` | Inlined as CTE, never materialized | Reusable logic shared across models | Cannot be queried directly | За замовчуванням використовується `view`. Перевизначити матеріалізацію можна у конфігураційному блоці моделі або у `dbt_project.yml`: ```sql -- models/marts/fct_daily_revenue.sql {{ config( materialized='incremental', unique_key='revenue_date', incremental_strategy='merge' ) }} SELECT DATE(order_date) AS revenue_date, SUM(order_amount) AS daily_revenue, COUNT(DISTINCT customer_id) AS unique_customers FROM {{ ref('stg_orders') }} WHERE order_status = 'completed' {% if is_incremental() %} AND order_date > (SELECT MAX(revenue_date) FROM {{ this }}) {% endif %} GROUP BY DATE(order_date) ``` Блок `is_incremental()` виконується лише під час інкрементальних запусків. При повному оновленні (`dbt run --full-refresh`) він пропускається, і таблиця перебудовується з нуля. ## Тестування якості даних на кожному рівні dbt надає дві категорії тестів: **generic-тести**, оголошені у YAML-файлах, та **singular-тести** — окремі SQL-файли, які повертають рядки у випадку порушення. Generic-тести покривають структурні обмеження: ```yaml # models/staging/_stg_models.yml version: 2 models: - name: stg_orders columns: - name: order_id tests: - unique - not_null - name: order_status tests: - accepted_values: values: ['completed', 'pending', 'cancelled', 'refunded'] - name: customer_id tests: - not_null - relationships: to: ref('stg_customers') field: customer_id ``` Singular-тести перевіряють бізнес-правила, які неможливо виразити за допомогою generic-тестів: ```sql -- tests/assert_revenue_never_negative.sql SELECT revenue_date, daily_revenue FROM {{ ref('fct_daily_revenue') }} WHERE daily_revenue < 0 ``` **Unit-тести**, запроваджені у dbt 1.8, дозволяють перевіряти логіку трансформацій з контрольованими вхідними даними та очікуваними результатами — без необхідності звертатися до сховища. Вони виконуються безпосередньо у фазі `dbt test`. Стратегія тестування повинна відповідати потоку даних: перевірки свіжості джерел та підрахунок рядків, schema-тести на staging-рівні (`not_null`, `unique`), перевірка референційної цілісності на intermediate-рівні та бізнес-асерції на рівні marts. ## dbt Core v1.10 та Fusion Engine dbt Core v1.10 запровадив прапорець `--sample` для команд `run` та `build`. Цей прапорець застосовує вибірку на основі часу до `ref()` та `source()`, дозволяючи розробникам валідувати трансформації на підмножині даних без витрат на повну побудову. Особливо корисно для ітерацій над моделями, що базуються на фактових таблицях з мільярдами рядків. [Fusion Engine](https://docs.getdbt.com/docs/dbt-versions/cloud-release-tracks) — повна переписка ядра dbt на Rust — тепер є стандартом для нових проєктів у dbt Cloud на Snowflake, BigQuery, Redshift та Databricks. Fusion запроваджує семантичне версіонування, починаючи з 2.0, та забезпечує суттєве покращення продуктивності компіляції та виконання. Серед інших помітних нововведень 2026 року: YAML-специфікація Semantic Layer для централізованого визначення метрик, Cost Insights (бета) для оцінки обчислювальних витрат сховища на модель, а також нативні приватні пакети, що тепер загальнодоступні. ## Макроси та Jinja для повторного використання логіки Коли один і той самий SQL-патерн зустрічається у кількох моделях, його варто винести у [макрос](https://docs.getdbt.com/docs/build/jinja-macros). Макроси — це Jinja-функції, що зберігаються у директорії `macros/`. ```sql -- macros/cents_to_dollars.sql {% macro cents_to_dollars(column_name) %} ROUND(CAST({{ column_name }} AS DECIMAL(10, 4)) / 100, 2) {% endmacro %} ``` Виклик у будь-якій моделі: ```sql -- models/staging/stg_payments.sql SELECT payment_id, order_id, {{ cents_to_dollars('amount_cents') }} AS amount_dollars, payment_method FROM {{ source('stripe', 'payments') }} ``` Такий підхід забезпечує дотримання принципу DRY. Альтернатива — копіювання формули конвертації у кожну модель, де вона потрібна — створює додаткові витрати на підтримку та ризик неузгодженості. ## Питання, які реально задають на співбесідах Співбесіди з data engineering у 2026 році все частіше перевіряють знання dbt за межами базових понять. Нижче наведено питання, які дозволяють відрізнити досвідчених кандидатів, зібрані на основі патернів, що зустрічаються на позиціях у компаніях із [сучасним стеком даних](/technologies/data-engineering). **П: Інкрементальна модель перебудувала всю таблицю вночі. Що сталося?** Найпоширеніша причина: колонка `unique_key` містила NULL-значення. Коли dbt намагається виконати MERGE за ключем із NULL, зіставлення не спрацьовує для жодного рядка, тому відбувається вставка дублікатів. Друга причина: хтось запустив `dbt run --full-refresh`, не усвідомлюючи, що це перебудовує таблицю з нуля. Третя: фільтр `is_incremental()` посилався на колонку, якої ще не існує у цільовій таблиці (перший запуск проти наступних). Діагностика починається з перевірки скомпільованого SQL у `target/compiled/`. **П: Як правильно розподілити тести у продакшн dbt-проєкті?** Перш за все виконуються перевірки свіжості джерел — якщо джерело не оновлювалося, downstream-моделі не повинні працювати на застарілих даних. Staging-тести валідують обмеження схеми: первинні ключі (`unique` + `not_null`), допустимі значення та приведення типів. Intermediate-тести перевіряють референційну цілісність між JOIN-операціями. Mart-тести перевіряють бізнес-інваріанти: виручка >= 0, активних користувачів >= платних користувачів, сума частин дорівнює загальному підсумку. Усі тести виконуються у CI на pull request-ах у dev-схемі, а потім повторно після деплою у продакшн з налаштованими алертами. **П: Коли модель має бути ephemeral, а коли view?** Ephemeral-моделі вбудовують свій SQL як CTE у моделі, що їх споживають — корисно для повторюваної логіки, яку не потрібно запитувати окремо. View обчислюються при читанні та існують як об'єкти, до яких можна звертатися. Компроміс: ephemeral-моделі не можна тестувати безпосередньо (вони не мають таблиці для виконання асерцій) і не відображаються в інструментах data lineage. Ephemeral варто використовувати для внутрішньої допоміжної логіки, а view — для всього, що потребує незалежного тестування або моніторингу. **П: Поясніть різницю між `source()` та `ref()`.** `source()` вказує на сирі таблиці, визначені у файлі `sources.yml` — це таблиці, якими dbt не управляє. `ref()` вказує на інші dbt-моделі. Обидві функції реєструють залежності у DAG, проте `source()` також дозволяє виконувати перевірки свіжості (`dbt source freshness`), чого `ref()` не підтримує. Використання `source()` для сирих таблиць і `ref()` для решти — це не рекомендація, а обов'язкова вимога, адже саме так dbt розрізняє те, чим він управляє, від того, чим ні. Для ширшого набору [питань зі співбесід з data engineering](/blog/data-engineering/data-engineering-interview-questions-2026), включаючи теми на кшталт [ETL vs ELT архітектури пайплайнів](/blog/data-engineering/etl-vs-elt-data-pipeline-architecture), модулі SharpSkill з [основ dbt](/technologies/data-engineering/interview-questions/dbt-fundamentals-de) та [просунутих dbt-патернів](/technologies/data-engineering/interview-questions/dbt-advanced-de) охоплюють ці концепції з інтерактивними вправами. ## Висновки - dbt трансформує сирі дані у сховищі через SQL SELECT-запити, які автоматично компілюються у DDL — пошарова структура staging/intermediate/mart забезпечує єдину відповідальність кожної моделі - Функція `ref()` є основою управління залежностями: вона розв'язує імена таблиць та будує DAG, що контролює порядок виконання - Інкрементальні матеріалізації зменшують витрати на великих таблицях, але потребують ретельної обробки `unique_key`, NULL-значень та фільтра `is_incremental()` - Тестування повинно відповідати потоку даних — свіжість джерел, обмеження схеми на staging-рівні, референційна цілісність на intermediate-рівні, бізнес-асерції на рівні marts - dbt Core v1.10 додає прапорець `--sample` для швидших ітерацій, а Fusion Engine (на базі Rust, версія 2.0) тепер є стандартом у dbt Cloud - Питання на співбесідах щодо dbt зосереджені на діагностиці реальних збоїв (повна перебудова інкрементальних моделей, NULL-ключі, застарілі джерела), а не на визначеннях — практичний досвід роботи зі скомпільованим SQL та CI-пайплайнами важить більше за заучені відповіді --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/data-engineering/dbt-data-transformations-testing-interview-2026