BigQuery dan DuckDB sama-sama mesin SQL analitik yang menyimpan data per kolom, tapi tinggal di tempat yang berlawanan. BigQuery adalah warehouse di cloud Google: gak ada server yang kamu urus, dan kamu bayar per byte data yang dibaca query. DuckDB adalah library yang jalan di dalam program kamu sendiri, di laptop, dan bisa langsung membaca file Parquet dan CSV. Bayangin cucian. BigQuery itu laundry kiloan: tinggal serahkan, mesinnya bukan urusanmu, bayarnya per kilo. DuckDB itu mesin cuci di rumah: gratis tiap kali pakai, tapi muatannya sebatas mesinmu.
🧪 Tebakan cepat: analis menjalankan
SELECT * FROM penjualan LIMIT 10di BigQuery untuk mengintip isi tabel. Tabelnya 1 miliar baris. Yang ditagih 10 baris atau seluruh tabel? Tebak dulu, jawabannya ada di bagian "BigQuery: bayar per byte yang dibaca".
Dua mesin columnar, dua tempat
| DuckDB | BigQuery | |
|---|---|---|
| Jalan di mana | di dalam proses Python, Node, atau CLI kamu (in-process) | di server Google (serverless: servernya ada, tapi bukan urusanmu) |
| Mulai pakai | pip install duckdb |
akun Google Cloud, project, dataset |
| Data | file Parquet, CSV, JSON di disk atau di S3, tabel DuckDB sendiri | tabel BigQuery (storage-nya ikut dibayar) |
| Biaya | gratis, open source | per byte yang dibaca query, plus storage |
| Skala | sebatas satu mesin: sampai ratusan GB masih nyaman | terabyte sampai petabyte, banyak orang sekaligus |
Keunggulan utama DuckDB dibanding warehouse cloud: dia jalan lokal tanpa server, jadi cocok untuk eksplorasi data di laptop developer, untuk test pipeline, dan untuk script yang mengolah file. Semua output DuckDB di pelajaran ini dari DuckDB 1.5.6 (rilis terbaru di PyPI per 11 Oktober 2026).
DuckDB: pertanyaan pemilik di laptop
Datanya order_lines.csv dan books.csv dari pelajaran pandas. Script ini membuat file penjualan.parquet (18 baris bersih, tanpa kolom minggu), lalu menjawab pertanyaan pemilik langsung dari file itu:
import duckdb
print(duckdb.__version__)
# 1. Extract jadi file Parquet: 18 baris bersih, tanpa server
duckdb.sql("""
COPY (
SELECT DISTINCT r.order_id, r.book_id,
CAST(r.created_at AS DATE) AS tanggal,
COALESCE(r.city, 'Tidak diketahui') AS kota,
r.title AS judul,
r.qty,
b.catalog_price AS harga
FROM 'order_lines.csv' r
JOIN 'books.csv' b USING (book_id)
) TO 'penjualan.parquet' (FORMAT parquet)
""")
# 2. Pertanyaan pemilik, langsung dari file Parquet
duckdb.sql("""
SELECT date_trunc('week', tanggal)::DATE AS minggu, kota, judul, SUM(qty) AS terjual
FROM 'penjualan.parquet'
WHERE tanggal BETWEEN '2026-09-28' AND '2026-10-11'
GROUP BY minggu, kota, judul
QUALIFY RANK() OVER (PARTITION BY minggu, kota ORDER BY terjual DESC) = 1
ORDER BY minggu, kota, judul
""").show()
1.5.6
┌────────────┬─────────────────┬──────────────────────┬─────────┐
│ minggu │ kota │ judul │ terjual │
│ date │ varchar │ varchar │ int128 │
├────────────┼─────────────────┼──────────────────────┼─────────┤
│ 2026-09-28 │ Bandung │ Bumi Manusia │ 2 │
│ 2026-09-28 │ Jakarta │ Laskar Pelangi │ 3 │
│ 2026-09-28 │ Surabaya │ Cantik Itu Luka │ 2 │
│ 2026-09-28 │ Tidak diketahui │ Laskar Pelangi │ 1 │
│ 2026-10-05 │ Bandung │ Ronggeng Dukuh Paruk │ 2 │
│ 2026-10-05 │ Jakarta │ Cantik Itu Luka │ 2 │
│ 2026-10-05 │ Surabaya │ Laskar Pelangi │ 1 │
│ 2026-10-05 │ Surabaya │ Ronggeng Dukuh Paruk │ 1 │
│ 2026-10-05 │ Tidak diketahui │ Bumi Manusia │ 1 │
└────────────┴─────────────────┴──────────────────────┴─────────┘
FROM 'order_lines.csv'danFROM 'penjualan.parquet': nama file ditulis seperti nama tabel. Gak ada import, gak ada server.COPY (...) TO ... (FORMAT parquet)menulis hasil query ke file.date_trunc('week', tanggal)membulatkan ke Senin minggunya. HasilnyaTIMESTAMP(2026-09-28 00:00:00), jadi diubah keDATEdengan::DATE.QUALIFYmenyaring hasil window function di query yang sama, tanpa CTE. Hasilnya 9 baris, sama dengan pelajaran SQL: Surabaya minggu 5 Oktober seri.- Kalau
GROUP BY minggu, kota, juduldigantiGROUP BY ALL, DuckDB 1.5.6 menolak:Binder Error: Combining QUALIFY with GROUP BY ALL is not supported yet. duckdb.sql(...).df()mengubah hasil jadi DataFrame pandas.SUMdi DuckDB bertipeint128, dan pandas menerimanya sebagai angka desimal (5.0); tulisSUM(qty)::BIGINTkalau mau bilangan bulat.
BigQuery: bayar per byte yang dibaca
Di BigQuery kamu gak menyewa server. Tiap query dihitung dari jumlah byte di kolom yang dibaca, dengan harga on-demand US$6,25 per TiB (1 TiB = 1.024 GiB), dan 1 TiB pertama tiap bulan gratis. Storage 10 GiB pertama tiap bulan juga gratis. Angka ini dari halaman resmi cloud.google.com/bigquery/pricing per 11 Oktober 2026, untuk region us-central1 (Iowa); region lain bisa beda harganya. Aturan lain di halaman yang sama:
- Ditagih per kolom yang disebut query, walaupun ada
LIMIT.SELECT * ... LIMIT 10membayar semua kolom di semua baris. Itu jawaban tebakannya: seluruh tabel. - Dibulatkan ke atas per MB, minimal 10 MB per tabel yang dibaca dan per query.
- Query yang error, atau yang hasilnya diambil dari cache, gratis.
Ukuran tiap nilai juga sudah ditentukan: INT64 dan DATE 8 byte, STRING 2 byte ditambah panjang teksnya. Untuk tabel penjualan dari pelajaran sebelumnya:
| Kolom | Tipe | Byte untuk 18 baris |
|---|---|---|
order_id, book_id, qty, harga |
INT64 |
144 per kolom |
tanggal, minggu |
DATE |
144 per kolom |
kota |
STRING |
182 |
judul |
STRING |
306 |
SELECT * membaca 1.352 byte, pertanyaan pemilik (tanggal, kota, judul, qty) 776 byte; dua-duanya ditagih minimal 10 MB, dan gratis selama masih di bawah 1 TiB sebulan. Bedanya baru terasa kalau tabelnya besar. Misalnya marketplace buku dengan 1 miliar baris penjualan selama 3 tahun (1.096 hari), dan dashboard yang menjalankan query tiap 5 menit (8.640 query sebulan):
| Query | Dibaca per query | Biaya per query | Sebulan, setelah 1 TiB gratis |
|---|---|---|---|
SELECT * |
69,95 GiB | US$0,43 | US$3.682,68 |
| 4 kolom yang perlu | 40,15 GiB | US$0,25 | US$2.111,06 |
| 4 kolom, 2 minggu terakhir, tabel dipartisi | 0,51 GiB | US$0,0031 | US$20,84 |
Partisi dan cluster: memotong byte
CREATE TABLE `toko-buku.gudang.penjualan` (
order_id INT64, book_id INT64,
tanggal DATE, minggu DATE,
kota STRING, judul STRING,
qty INT64, harga INT64
)
PARTITION BY tanggal
CLUSTER BY kota;
PARTITION BY tanggalmembagi tabel jadi satu partisi per hari. Query denganWHERE tanggal BETWEEN '2026-09-28' AND '2026-10-11'cuma membaca 14 partisi, bukan 1.096. Ini baris terakhir tabel di atas.CLUSTER BY kotamengurutkan isi tiap partisi menurut kota. Query denganWHERE kota = 'Jakarta'bisa melewati blok yang isinya kota lain.- Partisi cuma menolong kalau query-nya menyaring kolom partisi. Tanpa
WHERE tanggal ..., semua partisi tetap dibaca.
Satu pertanyaan, tiga dialek
SQL di tiap mesin mirip, tapi gak sama. Pertanyaan pemilik di tiga tempat:
| SQLite (Playground dan Lab) | DuckDB | BigQuery | |
|---|---|---|---|
| Senin awal minggu | date(tanggal, 'weekday 0', '-6 days') |
date_trunc('week', tanggal)::DATE |
DATE_TRUNC(tanggal, WEEK(MONDAY)) |
Menyaring hasil RANK() |
CTE, lalu WHERE di query luar |
QUALIFY |
QUALIFY |
| Nama tabel | penjualan |
'penjualan.parquet' |
`toko-buku.gudang.penjualan` |
Di BigQuery, urutan argumennya terbalik dari DuckDB, dan WEEK tanpa (MONDAY) mulai hari Minggu. date(...) di SQLite adalah singkatan strftime('%Y-%m-%d', ...). Versi BigQuery-nya (gak dijalankan di pelajaran ini, karena butuh akun Google Cloud):
SELECT DATE_TRUNC(tanggal, WEEK(MONDAY)) AS minggu, kota, judul, SUM(qty) AS terjual
FROM `toko-buku.gudang.penjualan`
WHERE tanggal BETWEEN '2026-09-28' AND '2026-10-11'
GROUP BY minggu, kota, judul
QUALIFY RANK() OVER (PARTITION BY DATE_TRUNC(tanggal, WEEK(MONDAY)), kota ORDER BY SUM(qty) DESC) = 1
ORDER BY minggu, kota, judul;
Versi SQLite-nya ada di Lab, dan jawabannya sama: 9 baris.
Kapan pilih yang mana
- DuckDB kalau datanya muat di satu mesin, kamu kerja sendiri atau tim kecil, atau butuh SQL analitik di dalam script dan test. Untuk Toko Buku, DuckDB jauh lebih dari cukup.
- BigQuery kalau datanya terabyte, banyak orang dan dashboard membaca data yang sama, dan tim gak mau mengurus server. Pasang batas byte per query (maximum bytes billed) supaya query nyasar gak jadi tagihan besar.
- Banyak tim memakai dua-duanya: DuckDB untuk mencoba query di laptop, BigQuery untuk produksi.
⚠️ Jebakan yang sering ketemu
SELECT * ... LIMIT 10di BigQuery.LIMITgak mengurangi tagihan. Sebut kolom yang perlu, atau pakai tab Preview di console BigQuery yang gratis.- Lupa menyaring kolom partisi. Tabel dipartisi per tanggal tapi query-nya tanpa
WHERE tanggal: semua partisi dibaca. - Awal minggu beda-beda.
WEEKdi BigQuery mulai Minggu;date_trunc('week', ...)di DuckDB mulai Senin. date_truncdi DuckDB mengembalikanTIMESTAMP. Tambah::DATEkalau yang kamu mau tanggal.
TL;DR: BigQuery dan DuckDB sama-sama columnar. DuckDB jalan di dalam program kamu tanpa server (pip install duckdb, versi 1.5.6), membaca Parquet dan CSV langsung (FROM 'penjualan.parquet'), gratis, dan cocok untuk eksplorasi di laptop. BigQuery adalah warehouse serverless di cloud: bayar US$6,25 per TiB yang dibaca (us-central1, per Oktober 2026), 1 TiB pertama tiap bulan gratis, minimal 10 MB per tabel, dan LIMIT gak mengurangi tagihan. Hemat byte dengan menyebut kolom yang perlu dan menyaring tabel yang dipartisi (PARTITION BY tanggal, CLUSTER BY kota): dashboard di 1 miliar baris turun dari US$3.682,68 jadi US$20,84 sebulan. Dialeknya beda: Senin awal minggu ditulis date(tanggal, 'weekday 0', '-6 days') di SQLite, date_trunc('week', tanggal) di DuckDB, DATE_TRUNC(tanggal, WEEK(MONDAY)) di BigQuery; DuckDB dan BigQuery punya QUALIFY, SQLite butuh CTE.