# 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. - 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) 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. > **Czym właściwie zajmuje się dbt** > > 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](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview), 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. ```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** — 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. ```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** — 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. ```sql -- 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](https://docs.getdbt.com/docs/build/materializations), 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`: ```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) ``` 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. ## 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: ```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 ``` Testy jednostkowe walidują reguły biznesowe, których testy generyczne nie są w stanie wyrazić: ```sql -- tests/assert_revenue_never_negative.sql SELECT revenue_date, daily_revenue FROM {{ ref('fct_daily_revenue') }} WHERE daily_revenue < 0 ``` **Unit 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](https://docs.getdbt.com/docs/dbt-versions/cloud-release-tracks), 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](https://docs.getdbt.com/docs/build/jinja-macros). Makra to funkcje Jinja przechowywane w katalogu `macros/`. ```sql -- 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: ```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') }} ``` 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](/technologies/data-engineering). **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](/blog/data-engineering/data-engineering-interview-questions-2026), obejmujący tematy takie jak [architektura potoków ETL vs ELT](/blog/data-engineering/etl-vs-elt-data-pipeline-architecture), jest dostępny w modułach ćwiczeniowych SharpSkill dotyczących [podstaw dbt](/technologies/data-engineering/interview-questions/dbt-fundamentals-de) oraz [zaawansowanych wzorców dbt](/technologies/data-engineering/interview-questions/dbt-advanced-de), które zawierają interaktywne zadania. ## 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 filtru `is_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ę `--sample` przyspieszają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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/data-engineering/dbt-data-transformations-testing-interview-2026