TL;DR
Pivot di SQL ngubah nilai baris jadi kolom pakai SUM dengan CASE WHEN, satu CASE per kolom target. Disebut dinamis kalau daftar kolomnya nggak diketik manual tapi dibangun otomatis dari data, biasanya lewat query yang ngerakit teks SQL pakai string_agg lalu dijalanin. Cocok buat laporan yang jumlah kategorinya berubah-ubah.
Pivot dinamis di SQL ngubah nilai baris jadi kolom tanpa kamu ngetik satu per satu nama kolomnya. Kolomnya ngikut data secara otomatis.
Kasus klasiknya: kamu punya data penjualan per produk per bulan dalam bentuk baris panjang, tapi bos minta tabel dengan produk di baris dan bulan jadi kolom.
Di sini kamu bakal belajar cara bikin pivot manual pakai CASE WHEN dulu, terus naik ke versi dinamis yang kolomnya kebentuk sendiri dari data.
Pivot di SQL adalah teknik ngubah nilai dari satu kolom jadi beberapa kolom baru, sambil ngagregasi angkanya. Data yang tadinya memanjang ke bawah (satu baris per bulan) jadi melebar ke samping (satu kolom per bulan). SQL standar nggak punya perintah PIVOT bawaan, jadi caranya pakai CASE WHEN di dalam fungsi agregasi.
Pivot disebut dinamis kalau daftar kolomnya nggak diketik manual, tapi dibangun otomatis dari isi data. Jadi kalau ada kategori baru muncul, kolomnya nambah sendiri.
Sebelum ke dinamis, pahamin dulu versi manualnya. Idenya: tiap kolom target dibikin dari satu SUM yang cuma ngitung baris di bulan tertentu, sisanya dianggap nol.
SELECT
produk,
SUM(CASE WHEN EXTRACT(MONTH FROM tanggal) = 1 THEN nominal ELSE 0 END) AS jan,
SUM(CASE WHEN EXTRACT(MONTH FROM tanggal) = 2 THEN nominal ELSE 0 END) AS feb,
SUM(CASE WHEN EXTRACT(MONTH FROM tanggal) = 3 THEN nominal ELSE 0 END) AS mar
FROM penjualan
GROUP BY produk
ORDER BY produk;
Tiap CASE WHEN ngecek bulannya. Kalau cocok, nilai nominal ikut dijumlah. Kalau nggak, dia nyumbang nol. Hasilnya tabel dengan produk di kiri dan kolom jan, feb, mar di kanan.
Kelemahannya jelas: kalau kamu butuh 12 bulan, kamu ngetik 12 baris CASE WHEN. Kalau kategorinya ratusan, ini nggak masuk akal.
Di sini triknya. SQL biasa nggak bisa bikin kolom dari hasil query dalam satu langkah, jadi prosesnya dua tahap. Pertama, rakit teks CASE WHEN-nya otomatis. Kedua, jalanin teks itu sebagai query.
Langkah pertama pakai string_agg buat nyambung potongan CASE WHEN buat tiap kategori unik.
SELECT string_agg(DISTINCT
format('SUM(CASE WHEN kategori = %L THEN nominal ELSE 0 END) AS %I',
kategori, kategori),
', ')
FROM penjualan;
Fungsi format ngisi %L dengan nilai kategori sebagai teks aman, dan %I sebagai nama kolom aman. Hasil query ini adalah satu string panjang berisi semua kolom CASE WHEN yang kamu butuh.
Langkah kedua, tempel string itu ke dalam query pivot lengkap. Di aplikasi nyata, kamu ambil hasilnya lewat kode (Python, misalnya) lalu susun query akhirnya.
SELECT produk, [hasil_string_agg_ditempel_di_sini]
FROM penjualan
GROUP BY produk;
Kalau kamu pengin semua dalam satu prosedur di PostgreSQL, kamu bisa bungkus pakai EXECUTE di dalam fungsi PL/pgSQL. Tapi buat mulai, dua tahap manual ini paling gampang dipahami.
Anggap toko_berkah, UMKM sembako di Bandung, punya tabel penjualan dengan kolom produk, kategori, tanggal, dan nominal. Ada 7 kategori dan pemiliknya mau rekap nominal per kategori dengan kategori jadi kolom.
Karena jumlah kategori bisa nambah tiap kali toko masukin lini baru, versi dinamis pas banget. Dari sampel data ngulikdata, pivot dinamis ini ngasilin tabel 7 kolom, dan yang menarik kategori 'sembako pokok' isinya Rp 71.500.000, hampir dua kali lipat kategori kedua terbesar yaitu 'minuman' di Rp 38.900.000.
Selisih setajam itu langsung kelihatan begitu data dilebarin jadi kolom. Kalau masih bentuk baris panjang, pola kayak gini lebih susah ditangkap sekilas.
Nggak mengamankan input kolom. Kalau kamu rakit query dari nilai data mentah tanpa format aman, kamu kebuka ke SQL injection. Pakai %L dan %I lewat fungsi format, jangan sambung string asal.
Lupa GROUP BY. Pivot itu agregasi. Tanpa GROUP BY produk, SUM-nya ngga kepisah per baris dan hasilnya campur aduk.
Maksa dinamis padahal kategorinya tetap. Kalau kategori kamu cuma 12 bulan yang nggak berubah, pivot statis lebih simpel dan gampang dibaca. Dinamis baru worth it kalau kategorinya beneran berubah-ubah. Buat dasar teknik ini, pahamin dulu conditional aggregation dengan CASE. Sintaks lengkap fungsi string ada di dokumentasi PostgreSQL.
Pertanyaan yang sering muncul soal pivot dinamis di SQL.
Pivot statis itu kamu ketik sendiri tiap kolomnya pakai CASE WHEN, misalnya kolom Januari sampai Desember. Cocok kalau kategorinya tetap. Pivot dinamis bikin daftar kolomnya otomatis dari data, jadi kalau muncul kategori baru, kolomnya ikut nambah tanpa kamu edit query. Dinamis lebih ribet disiapin, tapi hemat waktu buat data yang berubah-ubah.
SQL standar emang nggak punya kata kunci PIVOT bawaan, walau beberapa database kayak SQL Server dan Oracle punya sintaks sendiri. Di PostgreSQL dan MySQL, cara resminya pakai CASE WHEN di dalam agregasi. Pendekatan CASE WHEN ini justru paling portabel karena jalan di hampir semua database tanpa fitur khusus.
Kamu perlu dua langkah. Pertama, jalanin query yang ngerakit potongan teks CASE WHEN buat tiap kategori pakai string_agg. Kedua, ambil teks hasilnya dan tempel ke dalam query pivot yang beneran, lalu jalanin. Karena SQL biasa nggak bisa bikin kolom dari hasil query dalam satu langkah, prosesnya emang dua tahap.
Pivot di SQL masuk akal kalau datanya gede dan kamu mau ngurangin volume sebelum ditarik ke tools lain. Kalau datanya udah kecil dan kamu butuh interaktif, pivot table Excel atau pandas lebih fleksibel. Aturan praktisnya, biar SQL yang ngerjain agregasi berat, sisanya diserahin ke tools visual.
Fungsi crosstab dari ekstensi tablefunc bisa bikin pivot lebih ringkas, tapi butuh ekstensi dipasang dan sintaksnya lebih kaku soal tipe kolom. CASE WHEN lebih verbose tapi transparan dan jalan di mana aja. Buat mulai belajar, CASE WHEN lebih gampang dipahami karena kamu lihat jelas tiap kolom dibentuk dari mana.
Pivot di SQL bertumpu pada satu ide: SUM dengan CASE WHEN buat tiap kolom target. Versi dinamis tinggal ngerakit CASE WHEN itu otomatis pakai string_agg, lalu jalanin hasilnya sebagai query kedua.
Mau ringkasan multi level tanpa muter baris? Cek GROUPING SETS di SQL. Buat fondasi CASE WHEN-nya, baca conditional aggregation.
Pengin lancar nulis query rekap yang beneran dipakai di kerjaan analis? Latihan langsung di NgulikSQL, lengkap sama dataset toko biar kamu terbiasa sama kasus nyata.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Nambah 30 hari ke tanggal invoice, ngurangin sebulan buat cari periode lalu, ngitung selisih hari antar order. Semua itu aritmetika tanggal, dan SQL punya operator INTERVAL buat ngerjainnya.
Ngurangin tahun sekarang sama tahun lahir kelihatan gampang, tapi hasilnya sering meleset setahun kalau ulang tahunnya belum lewat. Ini cara hitung umur yang akurat di SQL.
Cara pakai DATE_TRUNC di SQL buat memotong timestamp jadi awal bulan, minggu, atau jam. Kunci bikin laporan penjualan per periode yang rapi.