1. Pengenalan PostgreSQL
PostgreSQL (sering disebut "Postgres") adalah sistem manajemen database relasional (RDBMS) open-source yang powerful dan sangat extensible. Dikembangkan sejak 1986 di University of California, Berkeley, PostgreSQL telah menjadi salah satu database paling populer dan paling dicintai oleh developer di seluruh dunia.
PostgreSQL dikenal karena kepatuhannya terhadap standar SQL, fitur-fitur canggih seperti JSON support, full-text search, dan kemampuan untuk menambahkan tipe data serta fungsi custom. Banyak perusahaan besar seperti Apple, Instagram, Spotify, dan NASA menggunakan PostgreSQL.
Mengapa Memilih PostgreSQL?
| Keunggulan | Penjelasan |
|---|---|
| ACID Compliant | Menjamin Atomicity, Consistency, Isolation, Durability untuk transaksi yang aman |
| Standar SQL | Sangat patuh terhadap standar SQL:2016 — query yang ditulis lebih portabel |
| JSON & JSONB | Dukungan native untuk data JSON — bisa digunakan sebagai database NoSQL juga |
| Extensible | Bisa menambah custom types, operators, functions, bahkan bahasa prosedural |
| Open Source | Lisensi PostgreSQL sangat liberal — bebas digunakan untuk tujuan apapun |
| Concurrency (MVCC) | Multi-Version Concurrency Control memungkinkan banyak user concurrent tanpa locking |
| Full-Text Search | Pencarian teks lengkap built-in tanpa perlu search engine tambahan |
PostgreSQL vs Database Lain
| Fitur | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| Tipe | RDBMS penuh | RDBMS | Embedded DB |
| Standar SQL | Sangat ketat | Cukup | Terbatas |
| JSON Support | ⭐ JSONB kaya fitur | Pemula | Terbatas |
| Concurrency | MVCC tanpa read lock | MVCC + table lock | Single writer |
| Cocok untuk | Aplikasi enterprise, GIS, analytics | Web app, CMS | Mobile, embedded |
PostgreSQL tersedia secara gratis di semua platform cloud besar — Amazon RDS, Google Cloud SQL, Azure Database, dan layanan khusus seperti Supabase dan Neon yang menawarkan PostgreSQL serverless dengan free tier.
2. Instalasi & Setup
Instalasi di Berbagai OS
-- INNER JOIN — hanya baris yang cocok di kedua tabel
SELECT m.nama AS mahasiswa, mk.nama_mk, n.nilai, n.grade
FROM nilai n
INNER JOIN mahasiswa m ON n.mahasiswa_id = m.id
INNER JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id
ORDER BY m.nama, mk.nama_mk;
-- LEFT JOIN — semua baris kiri + yang cocok dari kanan
SELECT m.nama AS mahasiswa, COUNT(n.id) AS jumlah_matkul
FROM mahasiswa m
LEFT JOIN nilai n ON m.id = n.mahasiswa_id
GROUP BY m.nama
ORDER BY jumlah_matkul DESC;
-- RIGHT JOIN — semua baris kanan + yang cocok dari kiri
SELECT mk.nama_mk, COUNT(n.id) AS jumlah_nilai
FROM nilai n
RIGHT JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id
GROUP BY mk.nama_mk;
-- FULL OUTER JOIN — semua baris dari kedua tabel
SELECT m.nama AS mahasiswa, mk.nama_mk, n.nilai
FROM mahasiswa m
FULL OUTER JOIN nilai n ON m.id = n.mahasiswa_id
FULL OUTER JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id;
-- SELF JOIN — join tabel dengan dirinya sendiri
-- Contoh: cari mahasiswa dengan IPK sama
SELECT a.nama AS mahasiswa_1, b.nama AS mahasiswa_2, a.ipk
FROM mahasiswa a
INNER JOIN mahasiswa b ON a.ipk = b.ipk AND a.id < b.id;
-- JOIN dengan aggregasi
SELECT
m.nama,
ROUND(AVG(n.nilai), 2) AS rata_nilai,
COUNT(n.id) AS jumlah_mk,
SUM(mk.sks) AS total_sks
FROM mahasiswa m
JOIN nilai n ON m.id = n.mahasiswa_id
JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id
GROUP BY m.nama
HAVING AVG(n.nilai) >= 80
ORDER BY rata_nilai DESC;
6. Indexes
Index adalah struktur data yang mempercepat proses pencarian data dalam tabel. Tanpa index, database harus melakukan full table scan — memeriksa setiap baris satu per satu. Dengan index, pencarian bisa dilakukan dalam waktu logaritmik (O(log n)).
Jenis Index di PostgreSQL
| Jenis Index | Algoritma | Cocok Untuk |
|---|---|---|
| B-tree | Binary tree (default) | Equality, range, sorting — paling umum |
| Hash | Hash table | Hanya equality comparison (=) |
| GIN | Generalized Inverted Index | Full-text search, array, JSONB |
| GiST | Generalized Search Tree | Spatial data (GIS), range types |
| BRIN | Block Range Index | Tabel besar dengan data terurut kronologis |
-- B-tree index (default) — untuk pencarian umum
CREATE INDEX idx_mahasiswa_nama ON mahasiswa(nama);
-- Composite index — index pada multiple kolom
CREATE INDEX idx_nilai_mhs_mk ON nilai(mahasiswa_id, mata_kuliah_id);
-- Unique index — memastikan nilai unik
CREATE UNIQUE INDEX idx_mahasiswa_nim ON mahasiswa(nim);
-- Partial index — hanya index baris yang memenuhi kondisi
CREATE INDEX idx_mahasiswa_aktif ON mahasiswa(nama)
WHERE aktif = TRUE;
-- GIN index untuk JSONB
CREATE INDEX idx_mahasiswa_alamat ON mahasiswa USING GIN(alamat);
-- GIN index untuk full-text search
CREATE INDEX idx_mahasiswa_nama_search ON mahasiswa
USING GIN(to_tsvector('indonesian', nama));
-- Cek index yang ada pada tabel
\d mahasiswa
-- Analisis query dengan EXPLAIN ANALYZE
EXPLAIN ANALYZE
SELECT * FROM mahasiswa WHERE nama = 'Budi Santoso';
-- Seq Scan on mahasiswa (before index): 0.045 ms
-- Index Scan using idx_mahasiswa_nama (after index): 0.012 ms
-- Hapus index
DROP INDEX idx_mahasiswa_nama;
-- Rebuild index (setelah banyak INSERT/UPDATE/DELETE)
REINDEX INDEX idx_mahasiswa_nama;
- Kolom yang sering digunakan di
WHERE,JOIN, atauORDER BY - Kolom dengan high cardinality (banyak nilai unik, seperti email)
- Jangan terlalu banyak index — setiap INSERT/UPDATE harus memperbarui index juga
- Gunakan
EXPLAIN ANALYZEuntuk memverifikasi index digunakan
7. Views
View adalah query tersimpan yang diperlakukan seperti tabel virtual. View tidak menyimpan data sendiri — ia menjalankan query setiap kali diakses. View berguna untuk menyederhanakan query kompleks dan mengontrol akses data.
-- View: Ringkasan nilai mahasiswa
CREATE VIEW v_ringkasan_nilai AS
SELECT
m.nim,
m.nama AS nama_mahasiswa,
mk.nama_mk,
mk.sks,
n.nilai,
n.grade,
d.nama AS nama_dosen
FROM nilai n
JOIN mahasiswa m ON n.mahasiswa_id = m.id
JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id
JOIN dosen d ON mk.dosen_id = d.id
ORDER BY m.nama, mk.nama_mk;
-- Menggunakan view seperti tabel biasa
SELECT * FROM v_ringkasan_nilai;
SELECT nama_mahasiswa, AVG(nilai) FROM v_ringkasan_nilai GROUP BY nama_mahasiswa;
-- View: IPK Mahasiswa
CREATE VIEW v_ipk_mahasiswa AS
SELECT
m.nim,
m.nama,
ROUND(AVG(n.nilai), 2) AS rata_nilai,
COUNT(n.id) AS jumlah_mk,
SUM(mk.sks) AS total_sks,
CASE
WHEN AVG(n.nilai) >= 85 THEN 'Cum Laude'
WHEN AVG(n.nilai) >= 75 THEN 'Baik'
ELSE 'Cukup'
END AS predikat
FROM mahasiswa m
JOIN nilai n ON m.id = n.mahasiswa_id
JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id
GROUP BY m.nim, m.nama
ORDER BY rata_nilai DESC;
-- Materialized View — menyimpan hasil query (perlu refresh)
CREATE MATERIALIZED VIEW mv_statistik_nilai AS
SELECT
mk.nama_mk,
COUNT(n.id) AS jumlah_nilai,
ROUND(AVG(n.nilai), 2) AS rata_nilai,
MAX(n.nilai) AS nilai_max,
MIN(n.nilai) AS nilai_min
FROM nilai n
JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id
GROUP BY mk.nama_mk;
-- Refresh materialized view (harus dilakukan manual)
REFRESH MATERIALIZED VIEW mv_statistik_nilai;
-- Update view
CREATE OR REPLACE VIEW v_ringkasan_nilai AS
SELECT
m.nim,
m.nama AS nama_mahasiswa,
m.ipk,
mk.nama_mk,
mk.sks,
n.nilai,
n.grade
FROM nilai n
JOIN mahasiswa m ON n.mahasiswa_id = m.id
JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id;
-- Hapus view
DROP VIEW IF EXISTS v_ringkasan_nilai;
8. Stored Procedures & Functions
Stored procedures dan functions memungkinkan Anda menyimpan logika bisnis langsung di database. Ini mempercepat eksekusi karena mengurangi round-trip antara aplikasi dan database.
Functions
-- Function sederhana: hitung IPK mahasiswa
CREATE OR REPLACE FUNCTION hitung_ipk(p_mahasiswa_id INTEGER)
RETURNS NUMERIC(3,2) AS $$
DECLARE
v_ipk NUMERIC(3,2);
BEGIN
SELECT ROUND(AVG(nilai) / 25.0, 2) INTO v_ipk
FROM nilai
WHERE mahasiswa_id = p_mahasiswa_id;
RETURN COALESCE(v_ipk, 0.00);
END;
$$ LANGUAGE plpgsql;
-- Menggunakan function
SELECT nama, hitung_ipk(id) AS ipk FROM mahasiswa;
-- Function: konversi nilai ke grade
CREATE OR REPLACE FUNCTION konversi_grade(p_nilai NUMERIC)
RETURNS VARCHAR(2) AS $$
BEGIN
RETURN CASE
WHEN p_nilai >= 90 THEN 'A'
WHEN p_nilai >= 85 THEN 'A-'
WHEN p_nilai >= 80 THEN 'B+'
WHEN p_nilai >= 75 THEN 'B'
WHEN p_nilai >= 70 THEN 'B-'
WHEN p_nilai >= 65 THEN 'C+'
WHEN p_nilai >= 60 THEN 'C'
WHEN p_nilai >= 50 THEN 'D'
ELSE 'E'
END;
END;
$$ LANGUAGE plpgsql;
-- Function: cek apakah mahasiswa bisa lulus
CREATE OR REPLACE FUNCTION cek_kelulusan(p_mahasiswa_id INTEGER)
RETURNS TABLE(nama TEXT, ipk NUMERIC, total_sks INTEGER, lulus BOOLEAN) AS $$
BEGIN
RETURN QUERY
SELECT
m.nama::TEXT,
hitung_ipk(m.id),
COALESCE(SUM(mk.sks), 0)::INTEGER,
(hitung_ipk(m.id) >= 2.00 AND COALESCE(SUM(mk.sks), 0) >= 144)
FROM mahasiswa m
LEFT JOIN nilai n ON m.id = n.mahasiswa_id
LEFT JOIN mata_kuliah mk ON n.mata_kuliah_id = mk.id
WHERE m.id = p_mahasiswa_id
GROUP BY m.id, m.nama;
END;
$$ LANGUAGE plpgsql;
-- Menggunakan function
SELECT * FROM cek_kelulusan(1);
Stored Procedures
-- Procedure: proses pemberian nilai
CREATE OR REPLACE PROCEDURE berikan_nilai(
p_mahasiswa_id INTEGER,
p_mata_kuliah_id INTEGER,
p_nilai NUMERIC
)
LANGUAGE plpgsql
AS $$
DECLARE
v_grade VARCHAR(2);
BEGIN
-- Konversi nilai ke grade
v_grade := konversi_grade(p_nilai);
-- Insert atau update nilai
INSERT INTO nilai (mahasiswa_id, mata_kuliah_id, nilai, grade)
VALUES (p_mahasiswa_id, p_mata_kuliah_id, p_nilai, v_grade)
ON CONFLICT (mahasiswa_id, mata_kuliah_id)
DO UPDATE SET
nilai = EXCLUDED.nilai,
grade = EXCLUDED.grade;
-- Update IPK mahasiswa
UPDATE mahasiswa
SET ipk = hitung_ipk(p_mahasiswa_id),
updated_at = CURRENT_TIMESTAMP
WHERE id = p_mahasiswa_id;
RAISE NOTICE 'Nilai % (grade: %) berhasil diberikan', p_nilai, v_grade;
END;
$$;
-- Menjalankan procedure
CALL berikan_nilai(1, 3, 92.50);
-- Trigger: otomatis update timestamp
CREATE OR REPLACE FUNCTION update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Pasang trigger pada tabel mahasiswa
CREATE TRIGGER trigger_update_timestamp
BEFORE UPDATE ON mahasiswa
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang PostgreSQL: