Fact table yang dibangun di Chapter Grain dalam Data Model mencatat kejadian: satu baris untuk satu transaksi penjualan. Namun banyak pertanyaan analitik menyasar keadaan, bukan kejadian — berapa sisa stok produk pada 3 Januari, berapa saldo poin pelanggan akhir bulan, atau sudah sampai tahap mana sebuah pesanan diproses. Pertanyaan seperti tersebut tidak bisa dijawab dari daftar transaksi tanpa menghitung ulang seluruh riwayat setiap kali. Snapshot table menyimpan keadaan pada titik waktu tertentu sehingga jawabannya tinggal dibaca.
Periodic Snapshot vs Accumulating Snapshot #
Periodic snapshot menangkap keadaan pada jadwal tetap: setiap malam, saldo stok seluruh produk ditulis satu baris per produk. Accumulating snapshot menyimpan perjalanan satu entitas dalam satu baris yang diperbarui setiap kali milestone tercapai: pesanan yang baru dibuat, lalu dibayar, lalu dikirim, tercatat sebagai satu baris yang kolom-kolom waktunya terisi bertahap.
| Aspek | Periodic Snapshot | Accumulating Snapshot |
|---|---|---|
| Isi satu baris | keadaan pada satu titik waktu | perjalanan satu entitas dari awal sampai akhir |
| Waktu tulis | jadwal teratur (harian, mingguan) | setiap milestone terjadi |
| Pola tulis | sisip baris baru per periode | perbarui baris yang sama berulang |
| Pertanyaan yang dijawab | berapa stok pada tanggal H | berapa lama pesanan menunggu pengiriman |
| Contoh | saldo rekening, stok gudang | status pesanan, proses klaim |
Full vs Incremental Snapshot #
Snapshot full menyalin ulang seluruh keadaan setiap periode. Dengan 5.000 produk aktif, tabel stok harian bertambah 5.000 baris per hari terlepas dari berapa produk yang benar-benar berubah. Pendekatan tersebut sederhana dan mudah dibangun ulang, tetapi biaya penyimpanan tumbuh cepat untuk dimensi besar.
Snapshot incremental hanya menulis keadaan yang berubah sejak periode terakhir, biasanya lewat pola upsert pada kunci (tanggal, kode_produk). Bila hanya 300 dari 5.000 produk berubah hari itu, hanya 300 baris yang ditulis. Imbalannya, pipeline harus mampu mendeteksi perubahan — misalnya memanfaatkan kolom updated_at di sumber atau pola CDC — dan kegagalan di tengah proses harus aman diulang, tema yang sudah dibahas pada Course Data Pipeline Engineering.
Pilihan strategi juga menentukan apa yang bisa dibuktikan. Snapshot full mencatat keadaan lengkap setiap periode — produk yang tidak berubah pun tetap muncul, sehingga tabel bisa dijadikan bukti audit keadaan pada tanggal tertentu. Snapshot incremental hanya menyimpan jejak perubahan, sehingga keadaan tanggal tertentu harus direkonstruksi dari baris perubahan terakhir sebelum tanggal tersebut.
Contoh: Snapshot Stok Harian #
Kita bangun periodic snapshot stok toko online selama tiga hari. Primary key gabungan (tanggal, kode_produk) membuat snapshot per tanggal tidak pernah berisi duplikat — menjalankan ulang snapshot tanggal yang sama dua kali tetap menghasilkan keadaan yang tunggal.
import sqlite3
koneksi = sqlite3.connect(":memory:")
cur = koneksi.cursor()
# --- Periodic snapshot: stok per produk diambil setiap malam ---
cur.execute("""
CREATE TABLE snapshot_stok (
tanggal TEXT NOT NULL,
kode_produk TEXT NOT NULL,
stok INTEGER NOT NULL,
PRIMARY KEY (tanggal, kode_produk)
)
""")
stok_harian = {
"2026-01-01": {"P-100": 50, "P-200": 10},
"2026-01-02": {"P-100": 45, "P-200": 8},
"2026-01-03": {"P-100": 40, "P-200": 0},
}
for tanggal, produk in stok_harian.items():
for kode, jumlah in produk.items():
cur.execute(
"INSERT INTO snapshot_stok VALUES (?, ?, ?)", (tanggal, kode, jumlah)
)
print("Agregat dari snapshot stok tiga hari:")
for baris in cur.execute(
"""SELECT kode_produk,
COUNT(*) AS jumlah_hari,
ROUND(AVG(stok), 1) AS rata_stok,
MIN(stok) AS stok_min
FROM snapshot_stok
GROUP BY kode_produk"""
):
print(" ", baris)Output yang Dihasilkan:
Agregat dari snapshot stok tiga hari:
('P-100', 3, 45.0, 40)
('P-200', 3, 6.0, 0)Mari kita telaah baris-baris kode di atas:
- Baris 7-14 (
CREATE TABLE snapshot_stok): primary key gabungan tanggal dan produk menegaskan grain snapshot — satu baris berarti keadaan satu produk pada satu hari; kunci ini sekaligus menjadi pengaman idempotent saat snapshot dijalankan ulang. - Baris 16-25 (pemuatan tiga hari): loop menulis keadaan setiap produk setiap hari; nilai
P-200yang mencapai 0 pada hari ketiga tetap direkam karena produk tanpa transaksi pun ikut di-snapshot. - Baris 28-36 (agregat): karena snapshot tersimpan per hari, rata-rata dan minimum stok dihitung dengan agregat biasa tanpa membuka ulang transaksi penjualan — inilah manfaat utama periodic snapshot.
Accumulating Snapshot: Status Pesanan #
Untuk status pesanan, accumulating snapshot memakai satu baris per pesanan dengan kolom waktu per milestone. Baris yang sama diperbarui ketika pesanan maju tahap, sehingga tabel selalu memperlihatkan posisi terkini setiap pesanan.
# --- Accumulating snapshot: satu baris per pesanan, diperbarui mengikuti siklus ---
cur.execute("""
CREATE TABLE snapshot_pesanan (
id_pesanan TEXT PRIMARY KEY,
status TEXT NOT NULL,
dibuat_pada TEXT,
dibayar_pada TEXT,
dikirim_pada TEXT
)
""")
cur.execute(
"INSERT INTO snapshot_pesanan VALUES ('ORD-1', 'dibuat', '2026-01-02 08:00', NULL, NULL)"
)
cur.execute(
"""UPDATE snapshot_pesanan
SET status = 'dibayar', dibayar_pada = '2026-01-02 08:05'
WHERE id_pesanan = 'ORD-1'"""
)
cur.execute(
"""UPDATE snapshot_pesanan
SET status = 'dikirim', dikirim_pada = '2026-01-03 09:00'
WHERE id_pesanan = 'ORD-1'"""
)
print()
print("Accumulating snapshot ORD-1 (satu baris, tiga pembaruan):")
print(" ", cur.execute("SELECT * FROM snapshot_pesanan").fetchall())
print()
print("Pesanan dengan jeda bayar ke kirim lebih dari sehari:")
for baris in cur.execute(
"""SELECT id_pesanan, dibayar_pada, dikirim_pada
FROM snapshot_pesanan
WHERE dikirim_pada IS NOT NULL
AND julianday(dikirim_pada) - julianday(dibayar_pada) > 1"""
):
print(" ", baris)Output yang Dihasilkan:
Accumulating snapshot ORD-1 (satu baris, tiga pembaruan):
[('ORD-1', 'dikirim', '2026-01-02 08:00', '2026-01-02 08:05', '2026-01-03 09:00')]
Pesanan dengan jeda bayar ke kirim lebih dari sehari:
('ORD-1', '2026-01-02 08:05', '2026-01-03 09:00')Tiga pernyataan SQL menyentuh baris ORD-1 — insert saat dibuat, update saat dibayar, update lagi saat dikirim — dan hasil akhirnya tetap satu baris dengan seluruh milestone terisi. Query terakhir menghitung durasi antar-milestone langsung dari kolom yang ada, memakai fungsi julianday yang mengubah timestamp menjadi jumlah hari (dokumentasinya di dokumentasi SQLite
); pola pembaruan berkala seperti ini juga lazim diotomatisasi lewat model incremental dbt di docs.getdbt.com
.
Warehouse inti kini punya fakta, dimensi, dan snapshot. Langkah berikutnya adalah memotongnya menjadi porsi yang siap saji per departemen — pekerjaan Chapter Data Marts.