SQL Style Guide: Cara Nulis Query yang Enak Dibaca Tim
TL;DR
SQL style guide adalah kumpulan aturan penulisan query supaya kodenya konsisten dan gampang dibaca orang lain. Yang paling berpengaruh: keyword huruf besar, nama kolom snake_case, satu kolom per baris di SELECT, CTE ketimbang subquery bertingkat, dan alias yang deskriptif. Aturan mana yang dipilih nggak sepenting konsistensinya — satu gaya yang dipakai semua orang lebih berharga dari gaya sempurna yang cuma dipakai satu orang.
SQL style guide adalah kumpulan aturan penulisan query supaya kodenya konsisten dan gampang dibaca orang lain.
Terdengar remeh sampai kamu buka query 90 baris yang kamu tulis 6 bulan lalu, dan butuh 20 menit cuma buat inget query itu ngapain.
Database nggak peduli sama format. Yang peduli manusia berikutnya yang buka file itu — dan seringnya, itu kamu sendiri.
Ini 10 aturan yang paling berpengaruh, urut dari yang efeknya paling besar.
Kenapa style guide SQL penting padahal query-nya jalan?
Tiga alasan konkret.
Review jadi lebih cepat. Kalau semua query di tim formatnya sama, reviewer bisa fokus ke logikanya, bukan ke di mana WHERE-nya nyempil.
Bug lebih gampang ketahuan. Kondisi JOIN yang salah nyempil di WHERE itu susah dilihat kalau semuanya satu baris panjang. Begitu dipecah per baris, langsung kelihatan.
Onboarding lebih murah. Analyst baru yang baca query dengan format konsisten bisa produktif dalam hari, bukan minggu.
Dan ini yang paling penting: aturan mana yang kamu pilih nggak sepenting konsistensinya. Tim yang semua orangnya pakai gaya "lumayan" ngalahin tim yang tiap orang punya gaya "sempurna" versinya sendiri.
1. Keyword huruf besar, nama huruf kecil
SELECT, FROM, WHERE, JOIN, GROUP BY — huruf besar semua. Nama tabel dan kolom huruf kecil.
-- Susah dibaca
select kota, sum(total_belanja) from transaksi where status='selesai' group by kota;
-- Kebaca
SELECT
kota,
SUM(total_belanja) AS revenue
FROM transaksi
WHERE status = 'selesai'
GROUP BY kota;
Kontras huruf besar-kecil bikin mata kamu langsung nemu struktur query tanpa harus baca kata per kata.
2. Satu kolom per baris di SELECT
Ini yang paling sering dilanggar, dan paling berasa bedanya.
-- Susah di-review, susah di-diff
SELECT id, nama, kota, tanggal_daftar, total_belanja, status, channel
FROM pelanggan;
-- Gampang nambah, hapus, dan lihat perubahannya di git
SELECT
id,
nama,
kota,
tanggal_daftar,
total_belanja,
status,
channel
FROM pelanggan;
Alasan praktisnya: kalau query kamu masuk git, nambah satu kolom cuma bikin satu baris berubah — bukan seluruh baris SELECT.
3. snake_case buat nama kolom dan tabel
total_belanja, bukan totalBelanja atau TotalBelanja.
Alasannya bukan estetika. Beberapa database (PostgreSQL, salah satunya) otomatis ngubah nama jadi huruf kecil semua kecuali kamu kasih tanda kutip. Jadi totalBelanja bakal jadi totalbelanja — kebaca kayak satu kata panjang.
Kolom boolean kasih awalan is_ atau has_: is_aktif, has_diskon. Kolom tanggal kasih akhiran yang jelas: tanggal_transaksi, bukan cuma tanggal.
4. CTE, bukan subquery bertingkat
Ini aturan yang paling ngubah kualitas hidup.
-- Baca dari dalam ke luar. Pusing.
SELECT kota, revenue
FROM (
SELECT kota, SUM(total) AS revenue
FROM (
SELECT p.kota, t.total_belanja AS total
FROM transaksi t
JOIN pelanggan p ON p.id = t.pelanggan_id
WHERE t.status = 'selesai'
) x
GROUP BY kota
) y
WHERE revenue > 100000000;
-- Baca dari atas ke bawah. Tiap blok punya nama.
WITH transaksi_selesai AS (
SELECT
pel.kota,
trx.total_belanja
FROM transaksi AS trx
JOIN pelanggan AS pel ON pel.id = trx.pelanggan_id
WHERE trx.status = 'selesai'
),
revenue_kota AS (
SELECT
kota,
SUM(total_belanja) AS revenue
FROM transaksi_selesai
GROUP BY kota
)
SELECT
kota,
revenue
FROM revenue_kota
WHERE revenue > 100000000;
Versi kedua lebih panjang. Dan jauh lebih cepat dipahami.
Bonusnya: kamu bisa jalanin tiap CTE satu per satu buat debug. Subquery bertingkat nggak bisa gitu.
5. Alias yang deskriptif, dan selalu pakai AS
Alias satu huruf kelihatan hemat sampai query kamu punya 5 tabel.
-- p itu produk atau pelanggan?
FROM transaksi t
JOIN transaksi_detail d ON d.transaksi_id = t.id
JOIN produk p ON p.id = d.produk_id
JOIN pelanggan p2 ON p2.id = t.pelanggan_id
-- Jelas
FROM transaksi AS trx
JOIN transaksi_detail AS det ON det.transaksi_id = trx.id
JOIN produk AS prd ON prd.id = det.produk_id
JOIN pelanggan AS pel ON pel.id = trx.pelanggan_id
Kata kunci AS itu opsional di banyak database. Tulis aja. Lebih eksplisit, dan konsisten antar orang.
6. Kondisi JOIN di ON, filter di WHERE
Ini bukan cuma soal gaya — ini bisa ngubah hasil.
-- LEFT JOIN yang diam-diam berubah jadi INNER JOIN
FROM pelanggan AS pel
LEFT JOIN transaksi AS trx ON trx.pelanggan_id = pel.id
WHERE trx.status = 'selesai' -- baris NULL kebuang, LEFT JOIN sia-sia
-- Yang bener
FROM pelanggan AS pel
LEFT JOIN transaksi AS trx
ON trx.pelanggan_id = pel.id
AND trx.status = 'selesai'
Filter di WHERE dieksekusi setelah JOIN. Kalau kolom yang difilter berasal dari tabel kanan LEFT JOIN, semua baris NULL kebuang — dan LEFT JOIN kamu jadi nggak ada gunanya.
Ini bug yang paling sering aku lihat di query orang, termasuk yang udah pengalaman.
7. Indentasi 4 spasi, konsisten
Empat spasi. Bukan tab, bukan 2, bukan campur.
Kondisi WHERE yang lebih dari satu, taruh AND di awal baris — bukan di akhir baris sebelumnya:
WHERE trx.status = 'selesai'
AND trx.total_belanja > 0
AND trx.tanggal_transaksi >= '2026-01-01'
Kenapa AND di depan? Karena waktu kamu mau matiin satu kondisi buat tes, kamu tinggal comment satu baris utuh. Kalau AND-nya di belakang, kamu harus edit dua baris.
8. Komentar buat "kenapa", bukan "apa"
-- Sampah. Kodenya udah bilang gini.
-- Ambil data penjualan
SELECT * FROM transaksi;
-- Berguna. Info ini nggak ada di kode.
-- Kecualiin akun QA — tim ujicoba pakai ini buat tes checkout,
-- ada ~200 transaksi palsu per bulan
WHERE trx.pelanggan_id NOT IN (
SELECT id FROM pelanggan WHERE is_test = TRUE
)
Aturannya: kalau komentar kamu cuma nerjemahin kode ke bahasa Indonesia, hapus. Kalau komentar kamu jelasin keputusan yang nggak obvious, simpan.
9. Jangan SELECT * di query final
Buat eksplorasi cepat, SELECT * nggak apa-apa. Buat query yang masuk dashboard atau pipeline, sebutin kolomnya.
Alasannya: kalau ada orang nambah kolom di tabel sumber, query kamu diam-diam ikut narik kolom baru itu. Dashboard bisa rusak, atau lebih parah, data sensitif ikut kesalin.
10. Urutan klausa yang konsisten
Selalu urut: WITH → SELECT → FROM → JOIN → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT.
Ini bukan pilihan — SQL emang butuh urutan itu. Tapi yang sering acak-acakan itu penempatan barisnya. Kasih baris kosong antar blok besar, biar mata bisa istirahat.
Contoh kasus: query toko_berkah sebelum dan sesudah
Ini query asli yang aku temuin di satu repo. Tugasnya: revenue per kota per bulan dengan kontribusi persen.
Sebelum:
select p.kota,date_trunc('month',t.tanggal_transaksi) as bln,sum(t.total_belanja) as rev,count(distinct t.id) as jml, round(100.0*sum(t.total_belanja)/sum(sum(t.total_belanja)) over (partition by date_trunc('month',t.tanggal_transaksi)),1) as kontrib from transaksi t join pelanggan p on p.id=t.pelanggan_id where t.status='selesai' and t.total_belanja>0 and t.pelanggan_id not in (select id from pelanggan where is_test=true) group by p.kota,date_trunc('month',t.tanggal_transaksi) order by bln desc,rev desc;
Satu baris. 6 hal terjadi di dalamnya. Butuh 4 menit buat ngerti.
Sesudah:
WITH transaksi_bersih AS (
SELECT
trx.id AS transaksi_id,
trx.pelanggan_id,
trx.tanggal_transaksi,
trx.total_belanja
FROM transaksi AS trx
WHERE trx.status = 'selesai'
AND trx.total_belanja > 0
-- kecualiin akun QA, dipakai buat ujicoba checkout
AND trx.pelanggan_id NOT IN (
SELECT id FROM pelanggan WHERE is_test = TRUE
)
),
revenue_per_kota AS (
SELECT
pel.kota,
DATE_TRUNC('month', tb.tanggal_transaksi) AS bulan,
SUM(tb.total_belanja) AS revenue,
COUNT(DISTINCT tb.transaksi_id) AS jumlah_transaksi
FROM transaksi_bersih AS tb
JOIN pelanggan AS pel ON pel.id = tb.pelanggan_id
GROUP BY
pel.kota,
DATE_TRUNC('month', tb.tanggal_transaksi)
)
SELECT
kota,
bulan,
revenue,
jumlah_transaksi,
ROUND(revenue / NULLIF(jumlah_transaksi, 0)) AS rata_rata_transaksi,
ROUND(
100.0 * revenue / SUM(revenue) OVER (PARTITION BY bulan),
1
) AS kontribusi_persen
FROM revenue_per_kota
ORDER BY
bulan DESC,
revenue DESC;
Dari 1 baris jadi 38. Waktu buat ngerti: turun dari 4 menit ke sekitar 40 detik.
Dan ada satu efek samping yang nggak aku duga. Waktu query-nya dipecah, aku baru sadar COUNT(DISTINCT t.id) di versi lama ngitung transaksi — tapi nama aliasnya jml, yang bikin orang lain ngira itu jumlah pelanggan. Dua orang di tim itu udah salah baca angka ini selama 2 bulan.
Format yang rapi bukan cuma bikin enak dibaca. Kadang dia yang nemuin bug-nya.
COUNT dan window function di query ini yang paling sering bikin salah paham kalau alias-nya males.
Kesalahan umum waktu nerapin style guide
Bikin aturan 40 poin. Nggak ada yang inget. Sepuluh cukup, dan taruh di README repo.
Ngerapiin semua query lama sekaligus. Buang-buang waktu. Terapin aturannya di query baru, dan rapiin query lama cuma waktu kamu kebetulan nyentuh file itu.
Debat gaya berminggu-minggu. Uppercase atau lowercase, 2 spasi atau 4 — pilih satu, tulis, lanjut kerja. Konsisten lebih penting dari benar.
Nggak pakai formatter otomatis. SQLFluff bisa cek dan benerin format sesuai aturan yang kamu set. Sekali dipasang di pre-commit, nggak ada lagi debat format di code review.
Buat referensi aturan yang lebih lengkap, SQL Style Guide-nya Simon Holywell yang paling sering dijadiin rujukan tim data.
FAQ
Keyword SQL harus huruf besar atau kecil?
Huruf besar untuk keyword, huruf kecil untuk nama tabel dan kolom. Ini bukan soal benar atau salah — SQL nggak peduli. Tapi kontras huruf besar dan kecil bikin mata kamu langsung nemu struktur query: mana SELECT, mana WHERE, mana JOIN. Di query 60 baris, itu ngirit beberapa detik tiap kali kamu baca ulang.
Lebih baik pakai CTE atau subquery?
CTE, di hampir semua kasus. Subquery bertingkat maksa kamu baca dari dalam ke luar, yang kebalikan dari cara orang baca. CTE dibaca dari atas ke bawah, tiap blok punya nama, dan kamu bisa jalanin per bagian buat debug. Satu-satunya alasan pakai subquery: query super sederhana yang cuma satu tingkat.
Perlu nggak nulis komentar di query SQL?
Perlu, tapi cuma buat "kenapa", bukan "apa". Komentar "ambil data penjualan" di atas SELECT itu sampah — kodenya udah bilang gitu. Komentar yang berguna: "kecualiin status test karena tim QA pakai akun ini buat ujicoba". Itu informasi yang nggak ada di kode dan bikin orang berikutnya nggak salah paham.
Apa aturan penamaan alias yang bagus?
Pakai nama yang kebaca, bukan satu huruf. Alias "t" dan "p" kelihatan hemat sampai query kamu punya 5 tabel dan kamu lupa "p" itu produk atau pelanggan. Pakai singkatan yang jelas: "trx" untuk transaksi, "pel" untuk pelanggan. Dan selalu tulis kata kunci AS — lebih eksplisit, dan konsisten antar orang.
Ada tool buat ngerapiin query SQL otomatis?
Ada beberapa. SQLFluff paling populer buat tim yang pakai dbt — dia bisa cek dan benerin format sesuai aturan yang kamu set. Banyak SQL editor modern juga punya format-on-save bawaan. Tapi tool cuma ngurus format, bukan penamaan atau struktur. Aturan soal alias dan CTE tetap harus disepakati manusia.
Penutup
Kalau kamu cuma mau ambil tiga aturan dari daftar ini: pakai CTE bukan subquery, satu kolom per baris di SELECT, dan alias yang deskriptif.
Tiga itu doang udah nutupin sebagian besar query yang bikin orang pusing.
Dan inget kasus jml tadi — alias yang males bikin dua orang salah baca angka selama 2 bulan.
Mau latihan nulis CTE yang rapi dari awal? Mampir ke panduan CTE di SQL. Buat query yang bakal dipakai berulang di tim, cek juga sistem kerja data analyst — di situ ada pola template query yang bisa dipakai ulang.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel Terkait
7 SQL Client Gratis Terbaik buat Analis Data (2026)
Nggak perlu bayar buat nulis query. Ini 7 SQL client gratis yang beneran dipakai analis, plus kelebihan dan kekurangannya masing-masing.
Take-Home Test SQL Data Analyst: Contoh Soal + Cara Ngerjainnya
Contoh soal take-home test SQL yang beneran dipakai perusahaan, dikerjain step by step — dari baca soal, nulis query, sampai nyusun insight yang bikin recruiter nengok.
Latihan Soal SQL Menengah: Window Function & CTE
8 soal SQL level menengah pakai window function dan CTE, lengkap dengan jawaban, penjelasan, dan jebakan yang sering bikin orang salah.