Episode ini membahas sisi hybrid PostgreSQL sebagai RDBMS sekaligus document store: perbedaan JSON dan JSONB, operator query JSONB seperti panah dan @>, fungsi manipulasi jsonb_set, serta tipe data array dengan operator ANY, @>, dan UNNEST.

Selamat datang di episode 8 series Belajar SQL PostgreSQL! Sejauh ini kita bermain dengan data yang sangat terstruktur: kolom, tipe, dan constraint yang kaku. Tapi dunia nyata tidak selalu rapi. Produk bisa punya atribut yang berbeda-beda (sepatu punya "ukuran", laptop punya "ram"), atau kalian perlu menyimpan daftar nilai dalam satu kolom (tags pada artikel). Untuk kebutuhan semacam inilah PostgreSQL punya senjata rahasia: tipe data JSONB dan Array.
Di episode ini, kita akan membahas perbedaan JSON dan JSONB, operator query JSON seperti ->, ->>, @>, dan ?, fungsi manipulasi seperti jsonb_set dan jsonb_array_elements, lalu pindah ke tipe data Array dengan operator ANY, @>, dan UNNEST.
Sebagian besar database memaksa kalian memilih: relasional (struktur ketat, join, transaction) atau document store (fleksibel, schema-less). PostgreSQL menjawab "kenapa tidak keduanya?" dengan mendukung JSONB secara native: data JSON bisa disimpan, diquery, di-index, bahkan digabung dengan relasi SQL dalam satu query.
Kapan JSONB cocok dipakai?
Kapan sebaiknya tidak memakai JSONB? Saat data itu selalu diquery, harus di-join, atau butuh constraint ketat. Aturan praktisnya: jika data itu "kolom", buat kolom. Jika itu "dokumen", pakai JSONB.
PostgreSQL punya dua tipe JSON: JSON dan JSONB.
| Aspek | JSON | JSONB |
|---|---|---|
| Penyimpanan | Teks apa adanya (preserve spasi, urutan key) | Binary, dinormalisasi, urutan key tidak dijamin |
| Kecepatan baca | Lambat (di-parse ulang tiap dibaca) | Cepat (sudah dalam bentuk binary) |
| Indexing | Tidak bisa di-index langsung | Bisa di-index (GIN index, episode 14) |
| Duplikat key | Dipertahankan | Yang terakhir menang |
| Kasus penggunaan | Log/arsip yang butuh struktur asli | Query dan analisis data JSON |
Best practice modern: selalu gunakan JSONB kecuali ada kebutuhan langka untuk melestarikan struktur teks mentah.
Tiga operator yang wajib kalian hafal sejak awal:
| Operator | Fungsi | Hasil |
|---|---|---|
-> | Ambil nilai key sebagai JSON | {"a": 1} -> 'a' → 1 (masih JSON) |
->> | Ambil nilai key sebagai teks | {"a": 1} ->> 'a' → 1 (teks) |
@> | Apakah JSON mengandung nilai? | '{"a":1,"b":2}' @> '{"a":1}' → TRUE |
? | Apakah key ada? | '{"a":1}' ? 'a' → TRUE |
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
attributes JSONB NOT NULL DEFAULT '{}'
);
INSERT INTO products (name, attributes) VALUES
('Laptop Pro', '{"ram": "16GB", "warna": "silver"}'),
('Sepatu Lari', '{"ukuran": 42, "warna": "hitam"}');
SELECT name, attributes ->> 'ram' AS ram
FROM products;->> 'ram' mengembalikan "16GB" sebagai teks untuk laptop, dan NULL untuk sepatu yang tidak punya key ram — fleksibilitas inilah inti JSONB.
JSONB bisa dipakai dalam WHERE secara langsung:
SELECT name
FROM products
WHERE attributes @> '{"warna": "hitam"}';
SELECT name
FROM products
WHERE attributes ? 'ram';Query pertama mencari semua produk yang mengandung warna hitam. Query kedua mencari semua produk yang punya atribut ram — apa pun nilainya.
jsonb_set(jsonb, path, nilai) memperbarui nilai pada path tertentu dan mengembalikan JSONB baru. Berguna untuk update bagian kecil dari dokumen:
UPDATE products
SET attributes = jsonb_set(attributes, '{warna}', '"merah"')
WHERE name = 'Laptop Pro'
RETURNING attributes;jsonb_array_elements mengubah array JSON menjadi baris-baris tabel — kebalikan dari menyimpan daftar:
SELECT jsonb_array_elements('["a", "b", "c"]'::JSONB) AS elemen;Pola ini sering dipakai saat data JSON harus di-join dengan tabel relasional atau diagregasi per elemen. Gabungkan dengan LATERAL untuk memproses per baris:
SELECT p.name, warna.value
FROM products p,
LATERAL jsonb_array_elements(p.attributes -> 'warna_list') AS warna;Selain JSONB, PostgreSQL punya tipe Array native: TEXT[], INT[], dan seterusnya. Array ideal untuk daftar nilai sederhana yang tidak butuh struktur.
CREATE TABLE articles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
tags TEXT[] NOT NULL DEFAULT '{}'
);
INSERT INTO articles (title, tags)
VALUES ('Belajar SQL', ARRAY['sql', 'database', 'postgresql']);Array bisa ditulis dengan sintaks ARRAY[...] atau literal '{...}'. Keduanya setara.
| Operator | Fungsi | Contoh |
|---|---|---|
ANY(...) | Nilai ada di dalam array | 'sql' = ANY(tags) |
@> | Array mengandung semua nilai | tags @> ARRAY['sql'] |
UNNEST(...) | Array menjadi baris tabel | UNNEST(tags) |
SELECT title FROM articles
WHERE 'sql' = ANY(tags);
SELECT title FROM articles
WHERE tags @> ARRAY['sql', 'database'];
SELECT title, UNNEST(tags) AS tag
FROM articles;Query ketiga menggunakan UNNEST untuk "meledakkan" array menjadi satu baris per elemen — sangat berguna untuk laporan per tag.
Warning
Jangan bingung antara ANY untuk array dan ANY untuk subquery: keduanya bekerja sama, tapi operan berbeda. Untuk array pakai bentuk nilai = ANY(array). Dan ingat — pencarian = ANY(...) di array tidak memanfaatkan index B-Tree biasa; butuh GIN index yang akan kita pelajari di episode 14.
| Kebutuhan | Pilihan |
|---|---|
| Daftar nilai sederhana (tags, roles) | Array — ringan dan native |
| Dokumen dengan struktur dinamis | JSONB — fleksibel dan bisa di-index |
| Data yang harus di-join dengan relasi | JSONB atau kolom relasional |
| Agregasi per elemen | Keduanya bisa via UNNEST / jsonb_array_elements |
Di episode 8 ini kita sudah membuka sisi hybrid PostgreSQL: memahami kapan memakai JSONB dan kapan memakai array, perbedaan JSON dan JSONB, operator query JSONB ->, ->>, @>, dan ?, fungsi manipulasi jsonb_set dan jsonb_array_elements, serta tipe data array dengan operator ANY, @>, dan UNNEST.
Inti yang harus dibawa pulang:
JSONB adalah pilihan modern — cepat, bisa di-index, cocok untuk dokumen fleksibel.-> mengambil JSON, ->> mengambil teks — jangan tertukar.jsonb_set mengupdate bagian dokumen; jsonb_array_elements mengubah array menjadi baris.TEXT[]) cocok untuk daftar nilai sederhana yang ringan.UNNEST adalah kunci untuk mengagregasi per elemen array.Di episode 9 selanjutnya, kita naik ke level analitik: Window Functions (Analitis Data Lanjutan) — mulai dari perbedaan agregasi GROUP BY dengan window functions, anatomi clause OVER(), window framing, hingga fungsi ranking ROW_NUMBER, RANK, DENSE_RANK, NTILE dan fungsi value LAG, LEAD, FIRST_VALUE, LAST_VALUE. Ini salah satu topik paling digemari di SQL tingkat lanjut.