Urutan Eksekusi Logis SELECT
Pertanyaan
"Kita menulis query dari
SELECTdulu, tapi database tidak memprosesnya dengan urutan itu. Jelaskan urutan eksekusi logis sebuah querySELECT, dan apa akibatnya buat cara kita menulis query."
Tabel pesanan sebagai ilustrasi:
| pesanan_id | kota | status | total |
|---|---|---|---|
| 1 | Jakarta | selesai | 250000 |
| 2 | Jakarta | selesai | 400000 |
| 3 | Bandung | batal | 300000 |
| 4 | Bandung | selesai | 150000 |
| 5 | Surabaya | selesai | 500000 |
| 6 | Jakarta | batal | 100000 |
| 7 | Surabaya | selesai | 200000 |
| 8 | Bandung | selesai | 120000 |
Jawaban Singkat
Urutan logisnya: FROM (termasuk JOIN), WHERE, GROUP BY, HAVING, SELECT (termasuk window function), DISTINCT, ORDER BY, lalu LIMIT. Akibatnya, alias yang dibuat di SELECT belum dikenal di WHERE, agregat tidak boleh di WHERE (pakai HAVING), dan window function tidak bisa difilter di WHERE. Sebaliknya ORDER BY datang setelah SELECT, jadi boleh memakai alias.
Pembahasan
| Urutan | Klausa | Yang terjadi |
|---|---|---|
| 1 | FROM/JOIN | Ambil tabel sumber dan gabungkan |
| 2 | WHERE | Saring baris mentah |
| 3 | GROUP BY | Kelompokkan baris yang lolos |
| 4 | HAVING | Saring kelompok berdasarkan hasil agregasi |
| 5 | SELECT | Hitung kolom, ekspresi, alias, window function |
| 6 | DISTINCT | Buang baris duplikat hasil SELECT |
| 7 | ORDER BY | Urutkan hasil (alias dari SELECT sudah tersedia) |
| 8 | LIMIT | Potong jumlah baris |
Ikuti query di bawah sesuai urutan itu:
FROM pesanan: 8 baris.WHERE status = 'selesai': pesanan 3 dan 6 (batal) dibuang, sisa 6 baris.GROUP BY kota: Jakarta (250000 + 400000), Bandung (150000 + 120000), Surabaya (500000 + 200000).HAVING SUM(total) > 300000: Bandung (270000) gugur.SELECT: muncul kolomkotadan aliasrevenue.ORDER BY revenue DESC: aliasrevenuesudah ada, jadi boleh dipakai di sini.LIMIT 2: ambil dua teratas.
Kata "logis" penting: optimizer boleh mengeksekusi secara fisik dengan urutan lain (misalnya mendorong filter lebih awal), asalkan hasilnya sama dengan urutan logis ini.
Query
SELECT kota, SUM(total) AS revenue FROM pesanan WHERE status = 'selesai' GROUP BY kota HAVING SUM(total) > 300000 ORDER BY revenue DESC LIMIT 2;
Contoh Output
| kota | revenue |
|---|---|
| Surabaya | 700000 |
| Jakarta | 650000 |
Jebakan Umum
- Menulis
WHERE revenue > 300000memakai alias dariSELECT. Di PostgreSQL, MySQL, dan BigQuery ini error karenaWHEREdiproses sebelum alias dibuat (DuckDB memaafkannya, tapi jangan jadikan kebiasaan). - Menaruh
SUM(total) > 300000diWHERE. Agregat belum dihitung pada tahapWHERE, jadi error. - Memfilter
ROW_NUMBER() OVER (...) = 1diWHERE. Window function baru dihitung di tahapSELECT; bungkus dulu dengan CTE atau subquery. - Mengira
LIMITdijalankan sebelumORDER BY. Kalau begitu, hasil "top 2" jadi acak.
Follow-up dari Interviewer
- "Di mana posisi window function?" Dihitung di tahap
SELECT, setelahWHERE,GROUP BY, danHAVING. Karena itu window function bisa membungkus agregat, misalnyaSUM(SUM(total)) OVER (). - "Kenapa
ORDER BYboleh pakai alias tapiGROUP BYtidak?" Menurut standar,GROUP BYdiproses sebelumSELECT. Banyak engine tetap mengizinkan alias diGROUP BYsebagai kemudahan, tapi itu ekstensi, bukan konsekuensi urutan logis. Jangan bergantung pada itu di interview. - "Apakah database benar-benar menjalankan
FROMdulu?" Secara logis iya. Secara fisik optimizer bisa menyusun ulang (predicate pushdown, join reordering), tapi hasil akhirnya harus sama.
Yang Sebenarnya Diuji
Model mental tentang cara SQL bekerja. Hampir semua error klasik (alias di WHERE, agregat di WHERE, window function di WHERE) bisa dijelaskan dari urutan ini, jadi interviewer memakainya untuk mengukur seberapa dalam pemahamanmu.
Catatan Dialect
- DuckDB cukup longgar: alias
SELECTbahkan boleh dipakai diWHERE,GROUP BY, danHAVING. Ini kemudahan khusus DuckDB, bukan standar. - MySQL dan BigQuery mengizinkan alias di
GROUP BYdanHAVING, tapi tidak diWHERE. PostgreSQL mengizinkannya diGROUP BYsaja. - Supaya query portabel, anggap alias hanya aman di
ORDER BY. - DuckDB, BigQuery, dan Snowflake punya
QUALIFY, tahap filter tambahan setelah window function dihitung.