BigQuery Lanjutan: Partisi, Clustering, dan Menekan Biaya Query
TL;DR
BigQuery on-demand nagih berdasarkan jumlah byte yang dipindai query kamu, bukan waktu eksekusi. Partisi motong tabel berdasarkan kolom tanggal atau integer, jadi query yang difilter per tanggal cuma baca partisi yang relevan. Clustering ngurutin data di dalam partisi berdasarkan sampai 4 kolom, bikin filter dan agregasi di kolom itu jauh lebih murah. Kombinasi keduanya plus kebiasaan nulis query yang benar bisa motong byte terpindai sampai lebih dari 90%.
BigQuery nagih kamu berdasarkan jumlah byte yang dipindai query, bukan berapa lama query-nya jalan. Query yang selesai dalam 2 detik bisa lebih mahal dari query yang jalan 30 detik, kalau dia baca lebih banyak kolom.
Ini yang bikin banyak tim kaget waktu lihat tagihan bulan pertama.
Dua alat utama buat nekan angka itu: partisi dan clustering. Keduanya diatur waktu bikin tabel, bukan waktu nulis query. Jadi kalau tabel kamu udah terlanjur jadi tanpa partisi, ya tiap query bakal baca semuanya.
Aku pakai dataset penjualan UMKM di sini. Tabel transaksi_mentah isinya 240 juta baris transaksi dari 2023 sampai 2025, ukurannya 61 GB.
Gimana BigQuery ngitung biaya query?
Model on-demand BigQuery nagih per byte yang diproses. Kamu dapat 1 TiB gratis per bulan, sisanya dihitung per TiB dengan harga yang beda-beda per region. Tarif resmi dan terbaru ada di halaman pricing BigQuery.
Yang penting kamu pahami soal cara hitungnya:
- BigQuery nyimpen data per kolom. Query yang cuma minta 3 kolom nggak baca kolom lainnya.
SELECT *baca semua kolom. Di tabel 40 kolom, ini bisa 10 kali lebih mahal dari yang kamu butuh.- Klausa
LIMITnggak ngurangin byte terpindai. BigQuery tetap baca semua data yang lolos filter, baru motong hasilnya. - Query yang hasilnya diambil dari cache gratis.
Poin ketiga sering bikin salah paham. SELECT * FROM tabel LIMIT 10 di tabel 61 GB tetap kena biaya 61 GB.
Cek dulu sebelum jalanin
Sebelum eksekusi, BigQuery Studio nampilin estimasi byte di pojok kanan atas editor. Biasakan lihat angka itu.
Kalau kamu pakai command line, ada flag khusus buat dry run:
bq query --use_legacy_sql=false --dry_run \
'SELECT kota, SUM(total_belanja)
FROM penjualan.transaksi_mentah
GROUP BY kota'
Perintah ini nggak jalanin query-nya, cuma ngasih tau berapa byte yang bakal diproses. Gratis.
Apa itu tabel partisi di BigQuery?
Tabel partisi adalah tabel yang dipecah jadi bagian-bagian lebih kecil berdasarkan satu kolom. Waktu kamu filter pakai kolom itu, BigQuery cuma baca bagian yang cocok dan lewatin sisanya. Byte terpindai turun sesuai proporsi partisi yang kepakai.
Ada tiga jenis partisi yang didukung:
| Jenis | Basis pemecahan | Cocok buat |
|---|---|---|
| Time-unit column | Kolom DATE, TIMESTAMP, atau DATETIME | Tabel transaksi, log, event |
| Ingestion time | Waktu baris masuk ke BigQuery | Streaming data tanpa kolom tanggal jelas |
| Integer range | Rentang nilai kolom integer | ID toko, kode wilayah numerik |
Buat data transaksi, partisi harian berdasarkan kolom tanggal hampir selalu pilihan yang benar.
Bikin tabel partisi
CREATE OR REPLACE TABLE penjualan.transaksi_harian
PARTITION BY DATE(waktu_transaksi)
OPTIONS (
partition_expiration_days = 730,
require_partition_filter = TRUE
)
AS
SELECT * FROM penjualan.transaksi_mentah;
Dua opsi di situ yang sering dilewatin orang.
partition_expiration_days otomatis hapus partisi yang lebih tua dari 730 hari. Ini nekan biaya penyimpanan tanpa kamu perlu bikin job pembersihan.
require_partition_filter bikin BigQuery nolak query yang nggak nyantumin filter partisi. Query tanpa WHERE DATE(waktu_transaksi) ... bakal error, bukan jalan mahal. Ini pengaman paling efektif buat tim yang anggotanya banyak.
Kalau tabelnya udah terlanjur ada
Kamu nggak bisa nambahin partisi ke tabel yang udah jadi. Harus bikin ulang.
CREATE TABLE penjualan.transaksi_baru
PARTITION BY DATE(waktu_transaksi)
CLUSTER BY kota, kategori_produk
AS SELECT * FROM penjualan.transaksi_mentah;
DROP TABLE penjualan.transaksi_mentah;
ALTER TABLE penjualan.transaksi_baru
RENAME TO transaksi_mentah;
Query CREATE TABLE AS SELECT itu sendiri kena biaya sekali, soalnya dia baca seluruh tabel lama. Anggap saja investasi. Kalau tabelnya dipakai puluhan query per hari, balik modal dalam hitungan hari.
Apa bedanya clustering sama partisi?
Partisi misahin data ke folder terpisah berdasarkan satu kolom. Clustering ngurutin data di dalam tiap partisi berdasarkan sampai 4 kolom, jadi baris dengan nilai mirip disimpan berdekatan.
Efeknya: waktu kamu filter atau agregasi pakai kolom cluster, BigQuery bisa lewatin blok data yang pasti nggak cocok.
| Aspek | Partisi | Clustering |
|---|---|---|
| Jumlah kolom | 1 | Sampai 4, urutannya penting |
| Tipe kolom | Date, timestamp, atau integer | Hampir semua tipe termasuk STRING |
| Estimasi biaya sebelum jalan | Akurat | Nggak akurat, penghematan baru ketahuan setelah jalan |
| Bisa dipaksa lewat opsi | Ya, require_partition_filter | Nggak ada |
Urutan kolom di CLUSTER BY menentukan efektivitasnya. BigQuery ngurutin dari kolom pertama dulu, baru kolom kedua di dalam grup itu, dan seterusnya.
Jadi kalau kamu tulis CLUSTER BY kota, kategori_produk, filter WHERE kota = 'Surabaya' dapat penghematan penuh. Filter WHERE kategori_produk = 'Sembako' doang tanpa nyebut kota dapat penghematan jauh lebih kecil.
Aturannya: urutkan dari kolom yang paling sering dipakai buat filter.
Bikin tabel dengan partisi dan clustering sekaligus
CREATE OR REPLACE TABLE penjualan.transaksi_partisi
PARTITION BY DATE(waktu_transaksi)
CLUSTER BY kota, kategori_produk
OPTIONS (require_partition_filter = TRUE)
AS
SELECT * FROM penjualan.transaksi_mentah;
Beda sama partisi, clustering bisa diubah di tabel yang udah ada:
ALTER TABLE penjualan.transaksi_partisi
SET OPTIONS (clustering_fields = ['kota', 'metode_bayar']);
Perubahan ini cuma berlaku buat data yang masuk setelahnya. Data lama tetap urutan lama sampai BigQuery ngerapiin sendiri di belakang layar.
Contoh kasus: dari 61 GB jadi 1,4 GB
Ini perbandingan nyata dari dataset penjualan UMKM tadi. Pertanyaannya sederhana: berapa omzet per kategori produk di Surabaya sepanjang September 2025?
Query versi asal-asalan:
SELECT kategori_produk, SUM(total_belanja) AS omzet
FROM penjualan.transaksi_mentah
WHERE FORMAT_DATE('%Y-%m', DATE(waktu_transaksi)) = '2025-09'
AND kota = 'Surabaya'
GROUP BY kategori_produk;
Query versi benar di tabel yang udah dipartisi dan di-cluster:
SELECT kategori_produk, SUM(total_belanja) AS omzet
FROM penjualan.transaksi_partisi
WHERE DATE(waktu_transaksi) BETWEEN '2025-09-01' AND '2025-09-30'
AND kota = 'Surabaya'
GROUP BY kategori_produk;
| Skenario | Byte diproses | Waktu |
|---|---|---|
| Tabel polos, SELECT * dulu baru filter | 61,0 GB | 18 detik |
| Tabel polos, kolom dibatasi | 9,8 GB | 7 detik |
| Partisi saja | 3,1 GB | 4 detik |
| Partisi + clustering kota | 1,4 GB | 2 detik |
Dari 61 GB ke 1,4 GB. Turun 97,7%.
Perhatiin juga versi asal-asalan tadi. Dia pakai FORMAT_DATE() di kolom partisi. Membungkus kolom partisi dengan fungsi bikin BigQuery nggak bisa mangkas partisi, jadi dia baca semuanya walaupun tabelnya udah dipartisi.
Ini jebakan nomor satu. Filter kolom partisi harus langsung, jangan dibungkus fungsi apa pun.
Tujuh kebiasaan yang nekan biaya query
- Sebut kolomnya, jangan
SELECT *. Ini penghematan terbesar dan paling gampang. Di tabel lebar, bedanya bisa 6 kali lipat. - Filter kolom partisi tanpa dibungkus fungsi. Pakai
WHERE tanggal >= '2025-09-01', bukanWHERE EXTRACT(MONTH FROM tanggal) = 9. - Nyalain
require_partition_filter. Pengaman otomatis buat seluruh tim. - Pasang batas biaya per query. Di setelan query, ada opsi maximum bytes billed. Query yang melebihi batas langsung gagal, nggak jalan diam-diam.
- Simpan hasil perantara ke tabel. Kalau lima laporan baca agregasi yang sama, hitung sekali, simpan ke tabel kecil, baca dari situ.
- Manfaatin cache. Query yang persis sama dalam 24 jam gratis, asal tabel sumbernya nggak berubah. Jangan tambahin
CURRENT_TIMESTAMP()ke query yang seharusnya bisa di-cache. - Pakai preview, bukan query, buat ngintip data. Tab preview di konsol BigQuery gratis.
SELECT * LIMIT 100nggak.
Detail lengkap soal cara mangkas partisi ada di dokumentasi query tabel partisi.
Kesalahan umum
Partisi di kolom yang jarang dipakai buat filter. Partisi cuma nolong kalau query kamu memang sering filter pakai kolom itu. Kalau 80% query kamu filter pakai kota bukan tanggal, partisi tanggal nggak banyak nolong.
Partisi terlalu halus. Ada batas 10.000 partisi per tabel. Partisi harian selama 27 tahun udah nabrak batas itu. Buat data yang sangat panjang rentangnya, pertimbangin partisi bulanan.
Cluster di kolom dengan nilai unik terlalu banyak. Clustering di kolom transaksi_id nggak berguna. Setiap nilai cuma muncul sekali, nggak ada blok yang bisa dilewatin.
Cluster di kolom dengan nilai terlalu sedikit. Kolom status yang isinya cuma "sukses" dan "gagal" juga nggak banyak nolong. Sasaran yang bagus: puluhan sampai ribuan nilai unik.
Ngandelin estimasi byte buat tabel cluster. Estimasi di editor nggak memperhitungkan clustering. Angka yang tampil bakal lebih besar dari tagihan sebenernya. Cek angka aslinya di detail job setelah query selesai.
Nggak mantau siapa yang habis paling banyak. BigQuery nyimpen riwayat semua job di INFORMATION_SCHEMA. Query ini nunjukin pengguna paling boros 30 hari terakhir:
SELECT
user_email,
COUNT(*) AS jumlah_query,
ROUND(SUM(total_bytes_billed) / POW(1024, 4), 2) AS total_tib
FROM `region-asia-southeast2`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
AND job_type = 'QUERY'
GROUP BY user_email
ORDER BY total_tib DESC
LIMIT 10;
Jalanin sekali sebulan. Biasanya 2 sampai 3 orang nyumbang sebagian besar tagihan, dan penyebabnya satu dashboard yang refresh tiap 5 menit.
Kapan pindah ke model kapasitas?
Model on-demand bayar per byte. Model kapasitas bayar per slot yang kamu pesan, berapa pun byte yang diproses.
Patokan kasarnya: kalau pemakaian bulanan kamu udah stabil dan tinggi, model kapasitas biasanya lebih murah dan lebih gampang diprediksi. Kalau pemakaiannya naik turun dan masih di bawah beberapa TiB per bulan, on-demand lebih masuk akal.
Rapiin dulu partisi dan query kamu sebelum mikir ganti model. Banyak tim yang ganti ke kapasitas padahal masalahnya cuma SELECT * di dashboard.
FAQ
Apakah LIMIT ngurangin biaya query BigQuery?
Nggak. LIMIT cuma motong jumlah baris yang ditampilin, bukan jumlah data yang dipindai. Query dengan LIMIT 10 di tabel 61 GB tetap kena biaya 61 GB kalau nggak ada filter partisi. Kalau kamu cuma mau ngintip isi tabel, pakai tab preview di konsol yang gratis.
Berapa maksimal kolom yang bisa di-cluster?
Empat kolom, dan urutannya menentukan. BigQuery ngurutin data dari kolom pertama dulu. Filter yang nyebut kolom pertama dapat penghematan paling besar. Filter yang cuma nyebut kolom keempat tanpa tiga kolom sebelumnya hampir nggak dapat manfaat.
Bisa nambahin partisi ke tabel yang udah ada?
Nggak bisa langsung. Kamu harus bikin tabel baru dengan CREATE TABLE AS SELECT yang udah pakai PARTITION BY, lalu ganti nama. Clustering beda, itu bisa diubah kapan saja lewat ALTER TABLE SET OPTIONS, tapi cuma berlaku buat data yang masuk setelahnya.
Gimana cara batasin biaya biar nggak kebablasan?
Ada tiga lapis. Pertama, setel maximum bytes billed di tiap query biar query mahal langsung gagal. Kedua, nyalain require_partition_filter di tabel besar. Ketiga, pasang kuota harian per pengguna atau per project di setelan kuota Google Cloud.
Apa clustering bikin penyimpanan jadi lebih mahal?
Nggak. Clustering nggak nambah biaya penyimpanan dan proses pengurutan ulang di belakang layar juga nggak ditagih ke kamu. Yang bisa nambah biaya cuma proses bikin ulang tabelnya sekali di awal.
Penutup
Tiga langkah yang paling cepat ngasih hasil. Ganti semua SELECT * di query rutin kamu. Bikin ulang tabel besar dengan partisi tanggal plus require_partition_filter. Tambahin clustering di kolom yang paling sering kamu filter.
Coba jalanin query INFORMATION_SCHEMA di atas minggu ini. Biasanya langsung ketahuan satu atau dua query yang nyedot setengah tagihan.
Mau latihan nulis query agregasi yang efisien? Coba soal interaktif di NgulikSQL. Buat konsep dasarnya, baca juga soal aggregate function dan Snowflake untuk analis kalau kamu lagi bandingin gudang data.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel terkait
Cara Membuat Pivot Dinamis di SQL (Panduan 2026)
Pivot dinamis di SQL ngubah baris jadi kolom tanpa kamu hardcode nama kolomnya. Ini cara bikinnya pakai CASE WHEN dan versi yang kolomnya ngikut data.
Menghitung YTD, QTD, dan MTD di SQL (Panduan 2026)
YTD, QTD, dan MTD ngukur total dari awal tahun, kuartal, atau bulan sampai hari ini. Ini cara ngitungnya di SQL pakai DATE_TRUNC, plus satu query gabungan.
Gap and Island Analysis di SQL
Gap and island analysis di SQL nyari deret data yang berturut-turut (island) dan celah yang bolong (gap). Cocok buat hitung streak login atau cari tanggal transaksi yang hilang.