Transformasi bergaya ELT menempatkan pekerjaan SQL di dalam warehouse, dan dbt (data build tool) adalah alat yang mempopulerkan cara kerja tersebut. Topik ini membahas tiga konsep inti dbt, yaitu model sebagai berkas SQL yang dimaterialisasi di warehouse, source sebagai deklarasi tabel mentah yang dibaca, dan ref sebagai penghubung antar model. Proyek dbt pertama ditampilkan lengkap dengan pohon berkasnya dan konfigurasinya, kemudian semantik kedua modelnya dijalankan sungguhan dengan DuckDB pada data kecil, sehingga cara ref dan source bekerja terlihat tanpa memasang dbt Core sama sekali.
Model: SELECT yang dimaterialisasi#
Model dbt (model) adalah berkas berisi satu pernyataan SELECT yang oleh dbt dibungkus menjadi pembuatan objek di warehouse, proses yang disebut materialisasi (materialization). Nama berkasnya menjadi nama relasi hasilnya, sehingga berkas stg_pesanan.sql menghasilkan relasi bernama stg_pesanan. Bentuk materialisasi ditentukan konfigurasi, dengan pilihan yang paling umum berupa view untuk model antara dan table untuk model akhir, sedangkan folder tempat model disimpan ditunjuk oleh kunci model-paths pada dbt_project.yml.
Proyek dbt pertama cukup berisi pohon berkas berikut beserta konfigurasinya.
analitik_pesanan/
├── dbt_project.yml
└── models/
├── staging/
│ ├── sources.yml
│ └── stg_pesanan.sql
└── marts/
└── ringkasan_pesanan.sqlname: analitik_pesanan
version: "1.0.0"
config-version: 2
profile: analitik_pesanan
model-paths: ["models"]
models:
analitik_pesanan:
staging:
+materialized: view
marts:
+materialized: tableKunci models pada berkas konfigurasi itu menetapkan materialisasi per folder, sehingga seluruh model pada folder staging menjadi view dan seluruh model pada folder marts menjadi table tanpa menuliskan konfigurasi di setiap berkas SQL. Konfigurasi profile menunjuk koneksi ke warehouse yang dikelola dbt, sedangkan name menjadi nama proyek yang dipakai dbt di seluruh berkas konfigurasinya. Dua hal tersebut diperiksa lebih dulu oleh perintah dbt debug sebelum model apa pun dibangun.
# tidak-dijalankan
# seluruh perintah dbt dijalankan dari folder proyek setelah dbt Core terpasang
dbt debug # memeriksa struktur proyek dan koneksi ke warehouse
dbt run # membangun seluruh model sesuai urutan dependensinyaSource: pintu masuk data mentah#
Source adalah deklarasi untuk tabel mentah yang tidak dibangun oleh dbt, misalnya tabel hasil replikasi dari basis data operasional. Deklarasinya ditulis pada berkas YAML, lalu model merujuknya lewat fungsi source() dengan menyebut nama sumber dan nama tabelnya. Pemakaian source membuat setiap model mencatat asal datanya, sehingga garis keturunan data (lineage) sampai ke tabel mentah tetap terbaca, dan perubahan nama tabel di sumber cukup diperbaiki pada satu tempat.
Sebuah sumber juga dapat membawa aturan pengujian dan konfigurasi kesegaran yang menandai tabel mentah yang terlambat diperbarui. Pada proyek contoh, tabel pelanggan ikut dideklarasikan sejak awal karena pengujian relationships pada topik Pengujian dan dokumentasi dbt memerlukan tabel pelanggan sebagai acuan. Dengan demikian, berkas sources.yml menjadi daftar tunggal atas seluruh tabel mentah yang dipakai proyek.
version: 2
sources:
- name: operasional
description: "Basis data OLTP yang direplikasi ke warehouse."
schema: operasional
tables:
- name: pesanan
description: "Baris pesanan mentah dari sistem transaksi."
- name: pelanggan
description: "Daftar pelanggan sebagai acuan kunci asing."-- tidak-dijalankan
-- model staging: satu SELECT yang dimaterialisasi dari sumber
select
id as id_pesanan,
tanggal as tanggal_pesanan,
id_pelanggan,
jumlah_item,
status
from {{ source('operasional', 'pesanan') }}Model stg_pesanan.sql memilih dan menamai ulang kolom yang dibutuhkan dari sumber operasional.pesanan. Model staging semacam ini menjadi satu-satunya lapisan yang membaca bentuk asli tabel mentah, sehingga model lain tidak pernah menyentuh tabel sumber secara langsung. Pemisahan ini membuat perubahan di sumber hanya menghantam satu tempat, yaitu model staging yang menamai ulang kolomnya.
Ref: dependensi antar model#
Fungsi ref() merujuk model lain dan sekaligus menjadi pernyataan dependensi. dbt membaca seluruh pemanggilan ref untuk menyusun DAG (directed acyclic graph), yaitu graf dependensi yang tidak membentuk lingkaran, kemudian membangun model dalam urutan yang memenuhi seluruh dependensinya. Model yang bergantung pada dua model lain otomatis dibangun setelah keduanya selesai, tanpa urutan yang ditulis manual. Karena itu, nama relasi model lain tidak pernah ditulis langsung pada FROM, melainkan selalu melalui ref.
-- tidak-dijalankan
-- model mart: membaca model lain lewat ref, bukan nama tabel mentah
select
tanggal_pesanan,
count(*) as jumlah_pesanan,
sum(jumlah_item) as total_item
from {{ ref('stg_pesanan') }}
group by tanggal_pesananPada model ringkasan_pesanan.sql, pemanggilan ref dengan argumen stg_pesanan menyatakan bahwa model ini bergantung pada model staging yang sudah ditampilkan. dbt mengurutkan pembangunannya secara otomatis, yaitu stg_pesanan lebih dahulu dan ringkasan_pesanan kemudian. Jika kelak sebuah model dimasukkan di antara keduanya, cukup argumen ref yang diubah, dan urutan pembangunannya mengikuti. Pemakaian ref juga membuat dbt tidak membangun model yang dependensinya gagal dibangun lebih dulu, karena kegagalan model staging menghentikan model yang bergantung padanya.
Demonstrasi semantik dengan DuckDB#
Perintah dbt tidak dapat dijalankan pada lingkungan kerja jalur ini karena dbt Core tidak terpasang, sedangkan mekanisme source dan ref dapat dibuktikan dengan menjalankan SQL model yang sama pada DuckDB. Skrip berikut membuat tabel sumber yang kecil, lalu menjalankan SQL kedua model persis sebagaimana tertulis, dengan satu perbedaan, yaitu pemanggilan source() dan ref() digantikan dengan nama relasi yang sesungguhnya. Penggantian itulah yang dbt lakukan sebelum SQL dikirim ke warehouse.
"""Simulasi semantik dua model dbt dengan DuckDB.
dbt merender pemanggilan source() dan ref() menjadi nama relasi yang
sesungguhnya sebelum SQL dikirim ke warehouse. Skrip ini menjalankan SQL
kedua model pada data kecil, dengan jinja sudah digantikan namanya.
"""
import duckdb
kon = duckdb.connect()
# sumber: tabel operasional yang biasanya disediakan replikasi data
kon.execute(
"""
create table pesanan as
select * from (values
(1, date '2026-11-01', 'C01', 3, 'BARU'),
(2, date '2026-11-01', 'C02', 1, 'BARU'),
(3, date '2026-11-02', 'C01', 2, 'SELESAI'),
(4, date '2026-11-02', 'C03', 5, 'SELESAI')
) as t(id, tanggal, id_pelanggan, jumlah_item, status)
"""
)
# model stg_pesanan: source('operasional', 'pesanan') menjadi tabel pesanan
kon.execute(
"""
create view stg_pesanan as
select
id as id_pesanan,
tanggal as tanggal_pesanan,
id_pelanggan,
jumlah_item,
status
from pesanan
"""
)
# model ringkasan_pesanan: ref('stg_pesanan') menjadi view stg_pesanan
kon.execute(
"""
create table ringkasan_pesanan as
select
tanggal_pesanan,
count(*) as jumlah_pesanan,
sum(jumlah_item) as total_item
from stg_pesanan
group by tanggal_pesanan
"""
)
print("model stg_pesanan (materialisasi view):")
print(f"{'id_pesanan':>10} {'tanggal_pesanan':>16} {'id_pelanggan':>13} "
f"{'jumlah_item':>12} {'status':>8}")
for id_, tanggal, pelanggan, jumlah, status in kon.execute(
"select * from stg_pesanan order by id_pesanan"
).fetchall():
print(f"{id_:>10} {str(tanggal):>16} {pelanggan:>13} {jumlah:>12} {status:>8}")
print()
print("model ringkasan_pesanan (materialisasi table):")
for tanggal, n_pesanan, total_item in kon.execute(
"""
select tanggal_pesanan, jumlah_pesanan, total_item
from ringkasan_pesanan
order by tanggal_pesanan
"""
).fetchall():
print(f"{tanggal} {n_pesanan} pesanan {total_item} item")model stg_pesanan (materialisasi view):
id_pesanan tanggal_pesanan id_pelanggan jumlah_item status
1 2026-11-01 C01 3 BARU
2 2026-11-01 C02 1 BARU
3 2026-11-02 C01 2 SELESAI
4 2026-11-02 C03 5 SELESAI
model ringkasan_pesanan (materialisasi table):
2026-11-01 2 pesanan 4 item
2026-11-02 2 pesanan 7 itemKeluaran memperlihatkan kedua model dengan isi yang benar. Perhitungan jumlah pesanan dan total item per tanggal hanya mungkin bila stg_pesanan sudah tersedia lebih dulu, dan urutan itulah yang dijaga dbt lewat ref. Pada proyek yang sesungguhnya, perintah dbt run membuat view stg_pesanan lalu table ringkasan_pesanan di warehouse, dengan bentuk materialisasinya dibaca dari dbt_project.yml. Simulasi ini sekaligus menegaskan bahwa model dbt hanyalah SQL biasa, karena tanpa jinja dan tanpa dbt, SQL-nya tetap dapat dieksekusi oleh basis data apa pun yang mendukungnya.
Kesalahan umum#
- Menulis nama relasi model lain secara langsung pada
FROM. Penyebabnya, kebiasaan SQL mendorong jalan pintas daripada menuliskan fungsi ref. Akibatnya, dbt tidak mengenali dependensinya, sehingga model dapat dibangun tanpa urutan yang benar dan DAG-nya terputus. Perbaikannya, seluruh rujukan antar model melalui ref, dan seluruh tabel mentah melalui source. - Membaca tabel mentah langsung dari model akhir. Penyebabnya, model mart ditulis lebih dulu sebelum lapisan staging disiapkan. Akibatnya, bentuk asli tabel sumber menyebar ke banyak model, dan perubahan nama kolom di sumber menghantam semuanya sekaligus. Perbaikannya, lalui data mentah lewat model staging, lalu model akhir hanya membaca model staging.
- Menganggap berkas model otomatis ter-update di warehouse. Penyebabnya, model berupa berkas SQL yang disunting pada editor, sehingga pekerjaan terasa selesai begitu berkasnya tersimpan. Akibatnya, relasi di warehouse masih berisi hasil run sebelumnya sampai dbt dijalankan lagi. Perbaikannya, jalankan
dbt runsetelah setiap perubahan model, atau ikat perintah itu pada jadwal orkestrator.
Ringkasan#
- Model dbt adalah berkas SELECT yang dimaterialisasi menjadi view atau table, dan materialisasi per folder dikonfigurasi pada dbt_project.yml.
- Source mendeklarasikan tabel mentah yang tidak dibangun dbt, sedangkan ref merujuk model lain dan sekaligus menjadi dasar penyusunan DAG.
- Simulasi DuckDB membuktikan semantiknya, yaitu pemanggilan source dan ref digantikan dengan nama relasi yang sesungguhnya, dan urutan pembangunan mengikuti dependensi ref.
Referensi#
- What is dbt? — dbt Developer Hub — Dokumentasi resmi dbt yang memperkenalkan model, source, dan alur transformasinya.
- ref — dbt Developer Hub — Dokumentasi fungsi ref beserta cara dbt menyusun dependensi antar model.
- source — dbt Developer Hub — Dokumentasi fungsi source untuk merujuk tabel yang tidak dibangun dbt.