dbt w 2026 roku: transformacje danych, testowanie i pytania rekrutacyjne
Praktyczny przewodnik po dbt: modelowanie warstwowe, materializacje, testowanie jakości danych, makra Jinja i pytania rekrutacyjne dla inżynierów danych 2026.

dbt (data build tool) stał się standardowym frameworkiem do transformacji danych wewnątrz nowoczesnych hurtowni — w 2026 roku korzysta z niego ponad 40 000 firm w środowiskach produkcyjnych. Niniejszy artykuł omawia kluczowe mechanizmy transformacji w dbt, strategie testowania jakości danych oraz pytania, które rzeczywiście pojawiają się na rozmowach kwalifikacyjnych z zakresu inżynierii danych.
dbt obsługuje literę T w podejściu ELT. Kompiluje instrukcje SQL SELECT do DDL (CREATE TABLE, CREATE VIEW, MERGE) i wykonuje je w hurtowni danych — Snowflake, BigQuery, Redshift lub Databricks. Kontrola wersji, rozwiązywanie zależności, testowanie i dokumentacja są wbudowane.
Modelowanie warstwowe: Staging, Intermediate i Marts
Dobrze zaprojektowany projekt dbt dzieli transformacje na trzy warstwy. Podejście to, spopularyzowane przez społeczność dbt, wymusza zasadę pojedynczej odpowiedzialności modeli i znacząco upraszcza debugowanie.
Staging — modele najbliższe surowym danym. Ich zakres jest ściśle ograniczony: zmiana nazw kolumn, rzutowanie typów i filtrowanie nieprawidłowych wierszy. Bez złączeń, bez agregacji.
-- 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 NULLIntermediate — modele stosujące logikę biznesową: złączenia, agregacje, funkcje okna. Odwołują się do modeli staging poprzez funkcję ref(), która rejestruje zależności w grafie DAG.
-- 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_nameMarts — warstwa końcowa przeznaczona do konsumpcji. Czyste, udokumentowane tabele, z których bezpośrednio korzystają dashboardy i analitycy.
Takie podejście warstwowe oznacza, że awaria źródła wpływa wyłącznie na warstwę staging, a nie na cały pipeline. Każda warstwa może być testowana niezależnie.
Funkcja ref() i rozwiązywanie grafu DAG
ref() to nie alias. Wywołanie {{ ref('stg_orders') }} wykonuje dwie operacje: rozwiązuje pełną kwalifikowaną nazwę tabeli w czasie kompilacji oraz rejestruje krawędź zależności w skierowanym grafie acyklicznym (DAG) dbt. Bez ref() dbt nie ma możliwości określenia kolejności wykonania modeli.
Częstym błędem jest kodowanie nazw tabel na sztywno zamiast korzystania z ref(). Powoduje to przerwanie śledzenia zależności i może prowadzić do sytuacji, w której modele uruchamiają się przed gotowością ich zależności upstream.
-- Bad: hardcoded reference, no dependency tracking
SELECT * FROM analytics.stg_orders
-- Good: ref() registers the dependency
SELECT * FROM {{ ref('stg_orders') }}Graf DAG napędza również funkcje takie jak dbt run --select stg_orders+, która uruchamia model i wszystko, co znajduje się poniżej w hierarchii zależności — przydatne przy ukierunkowanych przebudowach po zmianie schematu źródła.
Materializacje: wybór odpowiedniej strategii przechowywania
dbt oferuje cztery wbudowane materializacje, z których każda jest dostosowana do różnych wzorców dostępu i wolumenów danych.
| 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 |
Domyślną materializacją jest view. Zmiana następuje poprzez blok konfiguracyjny w modelu lub w pliku dbt_project.yml:
-- 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)Blok is_incremental() wykonuje się wyłącznie podczas przyrostowych buildów — przy pełnym odświeżeniu (dbt run --full-refresh) jest pomijany, a cała tabela zostaje przebudowana od podstaw.
Gotowy na rozmowy o Data Engineering?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Testowanie jakości danych na każdej warstwie
dbt udostępnia dwie kategorie testów: testy generyczne deklarowane w plikach YAML oraz testy jednostkowe (singular tests) zapisywane jako samodzielne pliki SQL, które zwracają wiersze w przypadku niepowodzenia.
Testy generyczne obejmują ograniczenia strukturalne:
# 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_idTesty jednostkowe walidują reguły biznesowe, których testy generyczne nie są w stanie wyrazić:
-- tests/assert_revenue_never_negative.sql
SELECT
revenue_date,
daily_revenue
FROM {{ ref('fct_daily_revenue') }}
WHERE daily_revenue < 0Unit tests, wprowadzone w dbt 1.8, umożliwiają walidację logiki transformacji z kontrolowanymi danymi wejściowymi i oczekiwanymi wynikami — bez konieczności korzystania z hurtowni. Wykonują się bezpośrednio w fazie dbt test.
Strategia testowania powinna podążać za przepływem danych: sprawdzanie świeżości źródeł i walidacja liczby wierszy, testy schematów na warstwie staging (not_null, unique), integralność referencyjna na warstwie intermediate oraz asercje logiki biznesowej na warstwie marts.
dbt Core v1.10 i silnik Fusion
dbt Core v1.10 wprowadził flagę --sample dla poleceń run i build. Flaga ta stosuje próbkowanie czasowe do ref() i source(), umożliwiając programistom walidację transformacji na podzbiorze danych bez kosztu pełnego buildu. Szczególnie przydatne przy iterowaniu nad modelami opartymi na tabelach faktów z miliardami wierszy.
Silnik dbt Fusion, napisany od podstaw w Rust, jest obecnie domyślnym silnikiem dla nowych projektów w dbt Cloud na Snowflake, BigQuery, Redshift i Databricks. Fusion wprowadza wersjonowanie semantyczne począwszy od wersji 2.0 i przynosi znaczące usprawnienia wydajności kompilacji i wykonywania.
Inne istotne nowości 2026 roku: specyfikacja Semantic Layer YAML do scentralizowanych definicji metryk, Cost Insights (beta) do szacowania kosztu obliczeniowego hurtowni dla poszczególnych modeli oraz natywne pakiety prywatne, które są teraz ogólnie dostępne.
Makra i Jinja jako logika wielokrotnego użytku
Gdy ten sam wzorzec SQL pojawia się w wielu modelach, należy wyekstrahować go do makra. Makra to funkcje Jinja przechowywane w katalogu macros/.
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
ROUND(CAST({{ column_name }} AS DECIMAL(10, 4)) / 100, 2)
{% endmacro %}Wywołanie w dowolnym modelu:
-- models/staging/stg_payments.sql
SELECT
payment_id,
order_id,
{{ cents_to_dollars('amount_cents') }} AS amount_dollars,
payment_method
FROM {{ source('stripe', 'payments') }}Takie podejście utrzymuje bazę kodu zgodną z zasadą DRY. Alternatywa — kopiowanie formuły konwersji do każdego modelu, który jej potrzebuje — generuje narzut utrzymaniowy i niespójności.
Pytania rekrutacyjne, które faktycznie padają na rozmowach
Rozmowy kwalifikacyjne z zakresu inżynierii danych w 2026 roku coraz częściej sprawdzają wiedzę o dbt wykraczającą poza podstawy. Poniżej przedstawiono pytania, które odróżniają kandydatów — opracowane na podstawie wzorców obserwowanych w firmach korzystających z nowoczesnych stosów danych.
P: Model przyrostowy przetworzyła ponownie całą tabelę w nocy. Co się stało?
Najczęstsza przyczyna: kolumna unique_key zawierała wartości NULL. Gdy dbt wykonuje operację MERGE na kluczu z wartością NULL, dopasowanie nie następuje dla żadnego wiersza, co skutkuje wstawieniem duplikatów. Druga przyczyna: ktoś uruchomił dbt run --full-refresh, nie zdając sobie sprawy, że przebudowuje tabelę od podstaw. Trzecia: filtr is_incremental() odwoływał się do kolumny, która jeszcze nie istnieje w tabeli docelowej (pierwsze uruchomienie vs kolejne). Debugowanie rozpoczyna się od sprawdzenia skompilowanego SQL w katalogu target/compiled/.
P: W jaki sposób powinno się warstwować testy w produkcyjnym projekcie dbt?
Najpierw uruchamiane są testy świeżości źródeł — jeśli źródło nie zostało zaktualizowane, modele downstream nie powinny przetwarzać nieaktualnych danych. Testy na warstwie staging walidują ograniczenia schematu: klucze główne (unique + not_null), dopuszczalne wartości i rzutowania typów. Testy na warstwie intermediate sprawdzają integralność referencyjną między złączeniami. Testy na warstwie marts weryfikują niezmienniki biznesowe: przychód >= 0, aktywni użytkownicy >= płacący użytkownicy, suma części równa się całości. Wszystkie testy uruchamiane są w CI na pull requestach wobec schematu deweloperskiego, a następnie ponownie po wdrożeniu w produkcji z alertingiem.
P: Kiedy model powinien być ephemeral, a kiedy view?
Modele ephemeral osadzają swój SQL jako CTE w modelach konsumujących — przydatne dla logiki wielokrotnego użytku, która nie musi być odpytywana niezależnie. Widoki (views) obliczają się przy odczycie i istnieją jako odpytywalne obiekty. Kompromis: modeli ephemeral nie da się testować bezpośrednio (nie mają tabeli, na której można uruchomić asercje) i nie pojawiają się w narzędziach do śledzenia liniowości danych. Ephemeral stosuje się dla wewnętrznej logiki pomocniczej; views dla wszystkiego, co wymaga niezależnego testowania lub monitoringu.
P: Wyjaśnij różnicę między source() a ref().
source() wskazuje na surowe tabele zdefiniowane w pliku sources.yml — są to tabele, którymi dbt nie zarządza. ref() wskazuje na inne modele dbt. Oba rejestrują zależności w grafie DAG, ale source() dodatkowo umożliwia sprawdzanie świeżości (dbt source freshness), czego ref() nie oferuje. Stosowanie source() dla surowych tabel i ref() dla wszystkiego innego nie jest opcjonalne — w ten sposób dbt wie, co kontroluje, a czego nie.
Szerszy zestaw pytań rekrutacyjnych z inżynierii danych, obejmujący tematy takie jak architektura potoków ETL vs ELT, jest dostępny w modułach ćwiczeniowych SharpSkill dotyczących podstaw dbt oraz zaawansowanych wzorców dbt, które zawierają interaktywne zadania.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Podsumowanie
- dbt transformuje surowe dane w hurtowni za pomocą instrukcji SQL SELECT kompilowanych automatycznie do DDL — warstwowa struktura staging/intermediate/marts zapewnia, że każdy model odpowiada za jedną odpowiedzialność
- Funkcja
ref()stanowi fundament zarządzania zależnościami: rozwiązuje nazwy tabel i buduje graf DAG kontrolujący kolejność wykonania - Materializacje przyrostowe (incremental) redukują koszty na dużych tabelach, ale wymagają starannej obsługi
unique_key, wartości NULL oraz filtruis_incremental() - Testowanie powinno podążać za przepływem danych — świeżość źródeł, ograniczenia schematu na warstwie staging, integralność referencyjna na warstwie intermediate, asercje logiki biznesowej na warstwie marts
- dbt Core v1.10 wprowadza flagę
--sampleprzyspieszającą iterację, a silnik Fusion (napisany w Rust, wersja 2.0) jest teraz domyślny w dbt Cloud - Pytania rekrutacyjne dotyczące dbt koncentrują się na debugowaniu rzeczywistych awarii (ponowne przetwarzanie przyrostowe, klucze NULL, nieaktualne źródła), a nie na definicjach — praktyczne doświadczenie ze skompilowanym SQL i potokami CI ma większe znaczenie niż wyuczone odpowiedzi
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Znajdziesz błąd w Data Engineering?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 25 maja 2026
Tagi
Udostępnij
Powiązane artykuły

ETL vs ELT w 2026: Architektura Potoków Danych i Pytania Rekrutacyjne
ETL i ELT to dwa podejścia do architektury potoków danych. To porównanie wyjaśnia, kiedy stosować każdy wzorzec, jakie narzędzia go wspierają oraz jakie pytania rekrutacyjne dotyczące projektowania potoków spotykają inżynierowie danych.

Apache Airflow w 2026: orkiestracja potoków danych, DAG-i i pytania rekrutacyjne
Kompleksowy przewodnik po Apache Airflow 3.2 w 2026 roku: tworzenie DAG-ów z Task SDK, dynamiczne mapowanie zadań, partycje assetów, natywne zadania asynchroniczne oraz pytania rekrutacyjne na stanowiska data engineering.

Apache Spark 4 w 2026 roku: Nowe funkcje, Structured Streaming i pytania rekrutacyjne
Kompleksowy przewodnik techniczny po Apache Spark 4 z omowieniem trybu ANSI SQL, typu danych VARIANT, Real-Time Mode Streaming, Spark Connect oraz najwazniejszych pytan rekrutacyjnych na stanowiska Data Engineering.