Data produk tidak pernah diam: warna diganti, stok bertambah, tag lama dibuang, spesifikasi baru menyusul. Kita sudah bisa menunjuk lokasi mana pun di dokumen lewat JSON Path — sekarang saatnya memakai alamat tersebut untuk mengubah isinya. MySQL menyediakan empat fungsi modifikasi — JSON_SET, JSON_INSERT, JSON_REPLACE, dan JSON_REMOVE — yang tampak mirip namun berbeda nasib satu sama lain tergantung ada atau tidaknya path yang dituju. Pada chapter ini kita membedah keempatnya pada satu dokumen yang sama, mempraktikkan pola UPDATE untuk menuliskan hasil perubahan ke kolom, lalu menutup dengan dua fungsi khusus array.
Menyiapkan Tabel Latihan #
Kita pakai kembali tabel produk(id, nama, atribut) di database belajar_json dengan tiga baris. Perhatikan bahwa dokumen tiap baris tidak seragam — Mug punya spesifikasi, Teh Melati tidak punya warna — karena perbedaan inilah pemilihan fungsi modifikasi jadi penting:
DROP DATABASE IF EXISTS belajar_json;
CREATE DATABASE belajar_json;
USE belajar_json;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE TABLE produk (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(100) NOT NULL,
atribut JSON NOT NULL
);
INSERT INTO produk (nama, atribut) VALUES
('Kopi Gayo 250g', '{"warna": "coklat", "berat": 250, "tag": ["arabika", "toraja"]}'),
('Mug Keramik', '{"warna": "putih", "berat": 350, "tag": ["keramik", "microwave"], "spesifikasi": {"bahan": "keramik", "kapasitas": "350 ml"}}'),
('Teh Melati 100g', '{"berat": 100, "tag": ["teh", "melati"]}');Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0Empat Fungsi, Satu Alamat #
Keempat fungsi modifikasi menerima argumen bentuk yang sama: dokumen sumber, satu atau lebih pasangan path-nilai, lalu mengembalikan dokumen baru. Yang membedakan adalah aturan mainnya terhadap keberadaan path:
| Fungsi | Path sudah ada | Path belum ada |
|---|---|---|
JSON_SET | Nilai ditimpa | Kunci baru dibuat |
JSON_INSERT | Diabaikan, nilai lama tetap | Kunci baru dibuat |
JSON_REPLACE | Nilai ditimpa | Diabaikan, tidak terjadi apa-apa |
JSON_REMOVE | Anggota/elemen dihapus | Diabaikan, tidak terjadi apa-apa |
Ringkasnya: JSON_SET berperilaku seperti “upsert” — pasang apa pun kondisinya; JSON_INSERT hanya berani menambah yang belum ada; JSON_REPLACE hanya berani menimpa yang sudah ada; JSON_REMOVE sekadar membuang. Memilih fungsi yang tepat mencegah dua kecelakaan klasik: tidak sengaja menimpa data lama, dan tidak sengaja membuat kunci baru yang tidak diinginkan.
Bedah Empat Fungsi pada Dokumen yang Sama #
Cara paling jujur membedakan keempat fungsi adalah menjalankannya pada dokumen identik. Variabel sesi @dok menampung satu dokumen uji berisi kunci warna dan tag yang sudah ada, serta kunci stok yang sengaja tidak ada — dokumen ini ditampilkan sekalian sebagai garis dasar pembanding, lalu langsung diuji dengan JSON_SET dan JSON_INSERT dalam satu blok yang sama (variabel sesi hanya hidup di dalam blok tempat ia diisi):
SET @dok = '{"berat": 250, "warna": "coklat", "tag": ["arabika", "toraja"]}';
SELECT @dok AS dokumen_asli;
SELECT
JSON_SET(@dok, '$.warna', 'merah') AS set_warna_ada,
JSON_SET(@dok, '$.stok', 15) AS set_stok_baru,
JSON_INSERT(@dok, '$.stok', 15) AS insert_stok_baru,
JSON_INSERT(@dok, '$.warna', 'merah') AS insert_warna_ada;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+-----------------------------------------------------------------+
| dokumen_asli |
+-----------------------------------------------------------------+
| {"berat": 250, "warna": "coklat", "tag": ["arabika", "toraja"]} |
+-----------------------------------------------------------------+
1 row in set (0.00 sec)
+----------------------------------------------------------------+-----------------------------------------------------------------------------+-----------------------------------------------------------------------------+-----------------------------------------------------------------+
| set_warna_ada | set_stok_baru | insert_stok_baru | insert_warna_ada |
+----------------------------------------------------------------+-----------------------------------------------------------------------------+-----------------------------------------------------------------------------+-----------------------------------------------------------------+
| {"tag": ["arabika", "toraja"], "berat": 250, "warna": "merah"} | {"tag": ["arabika", "toraja"], "stok": 15, "berat": 250, "warna": "coklat"} | {"tag": ["arabika", "toraja"], "stok": 15, "berat": 250, "warna": "coklat"} | {"tag": ["arabika", "toraja"], "berat": 250, "warna": "coklat"} |
+----------------------------------------------------------------+-----------------------------------------------------------------------------+-----------------------------------------------------------------------------+-----------------------------------------------------------------+
1 row in set (0.00 sec)Empat kolom hasil memperlihatkan empat nasib. Kolom set_warna_ada menampilkan warna yang sudah berubah menjadi merah — JSON_SET menimpa tanpa bertanya. Kolom set_stok_baru menampilkan kunci stok baru bernilai 15 — JSON_SET juga berani membuat kunci. Kolom insert_stok_baru berhasil untuk permintaan yang sama karena kunci memang belum ada. Kolom terakhir, insert_warna_ada, menampilkan dokumen yang sama persis dengan aslinya — JSON_INSERT menolak menimpa warna yang sudah terisi, sehingga permintaan 'merah' diabaikan diam-diam.
Blok berikutnya menguji JSON_REPLACE dan JSON_REMOVE pada dokumen yang sama. Variabel @dok diisi ulang karena variabel sesi tidak mengalir antarblok:
SET @dok = '{"berat": 250, "warna": "coklat", "tag": ["arabika", "toraja"]}';
SELECT
JSON_REPLACE(@dok, '$.warna', 'merah') AS replace_warna_ada,
JSON_REPLACE(@dok, '$.stok', 15) AS replace_stok_baru,
JSON_REMOVE(@dok, '$.warna') AS remove_warna,
JSON_REMOVE(@dok, '$.tag[0]') AS remove_elemen_tag;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+----------------------------------------------------------------+-----------------------------------------------------------------+----------------------------------------------+------------------------------------------------------+
| replace_warna_ada | replace_stok_baru | remove_warna | remove_elemen_tag |
+----------------------------------------------------------------+-----------------------------------------------------------------+----------------------------------------------+------------------------------------------------------+
| {"tag": ["arabika", "toraja"], "berat": 250, "warna": "merah"} | {"tag": ["arabika", "toraja"], "berat": 250, "warna": "coklat"} | {"tag": ["arabika", "toraja"], "berat": 250} | {"tag": ["toraja"], "berat": 250, "warna": "coklat"} |
+----------------------------------------------------------------+-----------------------------------------------------------------+----------------------------------------------+------------------------------------------------------+
1 row in set (0.00 sec)Kolom replace_warna_ada menunjukkan warna berubah menjadi merah — JSON_REPLACE menimpa kunci yang ada. Sebaliknya replace_stok_baru menampilkan dokumen tanpa stok sama sekali: permintaan membuat kunci baru ditolak karena JSON_REPLACE hanya bekerja pada yang sudah ada. Dua kolom JSON_REMOVE membedah penghapusan: remove_warna membuang kunci warna dari object, sedangkan remove_elemen_tag membuang elemen indeks 0 sehingga array tag menyisakan toraja saja — penghapusan elemen array menggeser elemen sisanya ke depan, sama seperti perilaku DELETE pada indeks array di bahasa pemrograman.
Mengubah Baris di Tabel: Pola UPDATE #
Menjalankan fungsi di SELECT hanya mengintip hasil tanpa menyimpannya. Pola penyimpanan yang benar membungkus fungsi di dalam UPDATE, dan kebiasaan yang baik menampilkan kondisi sebelum dan sesudahnya. Mari kita ubah warna Mug Keramik menjadi abu-abu:
SELECT nama, atribut FROM produk WHERE id = 2;Output yang Dihasilkan:
+-------------+-------------------------------------------------------------------------------------------------------------------------------+
| nama | atribut |
+-------------+-------------------------------------------------------------------------------------------------------------------------------+
| Mug Keramik | {"tag": ["keramik", "microwave"], "berat": 350, "warna": "putih", "spesifikasi": {"bahan": "keramik", "kapasitas": "350 ml"}} |
+-------------+-------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)UPDATE produk
SET atribut = JSON_SET(atribut, '$.warna', 'abu-abu')
WHERE id = 2;Output yang Dihasilkan:
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0SELECT nama, atribut FROM produk WHERE id = 2;Output yang Dihasilkan:
+-------------+---------------------------------------------------------------------------------------------------------------------------------+
| nama | atribut |
+-------------+---------------------------------------------------------------------------------------------------------------------------------+
| Mug Keramik | {"tag": ["keramik", "microwave"], "berat": 350, "warna": "abu-abu", "spesifikasi": {"bahan": "keramik", "kapasitas": "350 ml"}} |
+-------------+---------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)Mari kita bedah alurnya langkah demi langkah. SELECT pertama menampilkan dokumen dengan warna lama "putih" sebagai garis dasar. UPDATE kemudian mengevaluasi JSON_SET(atribut, '$.warna', 'abu-abu') untuk baris id = 2: fungsi membaca dokumen lama, menimpa nilai di path $.warna, dan mengembalikan dokumen hasil; operator = menugaskan dokumen hasil itu kembali ke kolom atribut. SELECT kedua membuktikan warna kini terbaca "abu-abu" — kunci lain seperti spesifikasi utuh karena JSON_SET bekerja bedah presisi, bukan menimpa dokumen keseluruhan. Perlu dicatat pula: mengubah satu nilai JSON membuat MySQL menulis ulang seluruh dokumen kolom tersebut, meski hanya satu karakter yang berbeda.
Skenario kedua memakai JSON_INSERT untuk menambah stok pada Kopi Gayo — aman diulang berkali-kali karena fungsi ini tidak pernah menimpa kunci yang sudah ada:
UPDATE produk
SET atribut = JSON_INSERT(atribut, '$.stok', 15)
WHERE id = 1;
SELECT nama, atribut FROM produk WHERE id = 1;Output yang Dihasilkan:
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
+----------------+-----------------------------------------------------------------------------+
| nama | atribut |
+----------------+-----------------------------------------------------------------------------+
| Kopi Gayo 250g | {"tag": ["arabika", "toraja"], "stok": 15, "berat": 250, "warna": "coklat"} |
+----------------+-----------------------------------------------------------------------------+
1 row in set (0.00 sec)Dokumen Kopi Gayo kini memuat kunci baru stok bernilai 15 di samping anggota lamanya. Menjalankan UPDATE yang sama untuk kedua kalinya tidak akan mengubah apa pun — JSON_INSERT mendapati stok sudah ada lalu mengabaikan nilai barunya, sehingga pola “pastikan atribut ada” ini idempoten. Perilaku berbeda dari JSON_SET: ulangi perintah set dan nilai akan tertimpa terus setiap kali dijalankan.
Menambah dan Menyisipkan Elemen Array #
Dua fungsi modifikasi khusus array melengkapi keluarga besar di atas. JSON_ARRAY_APPEND menambahkan elemen di ujung array, sedangkan JSON_ARRAY_INSERT menyisipkan elemen pada indeks yang kita tunjuk dan menggeser elemen lama ke belakang:
SELECT
JSON_ARRAY_APPEND(atribut, '$.tag', 'premium') AS tambah_belakang,
JSON_ARRAY_INSERT(atribut, '$.tag[0]', 'favorit') AS sisip_depan
FROM produk
WHERE id = 1;Output yang Dihasilkan:
+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+
| tambah_belakang | sisip_depan |
+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+
| {"tag": ["arabika", "toraja", "premium"], "stok": 15, "berat": 250, "warna": "coklat"} | {"tag": ["favorit", "arabika", "toraja"], "stok": 15, "berat": 250, "warna": "coklat"} |
+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+
1 row in set (0.00 sec)Kolom tambah_belakang menampilkan array tag yang kini berakhir dengan premium, sedangkan kolom sisip_depan menampilkan favorit menempati indeks 0 dan arabika terdorong ke indeks 1. Keduanya juga mengembalikan dokumen baru tanpa mengubah kolom — untuk menyimpannya, bungkus dengan pola UPDATE ... SET atribut = JSON_ARRAY_APPEND(...) sebagaimana contoh JSON_SET di atas.
Membersihkan Database Latihan #
Latihan memodifikasi dokumen selesai dan database latihan kita jatuhkan:
DROP DATABASE belajar_json;Output yang Dihasilkan:
Query OK, 1 row affected (0.00 sec)Referensi #
- JSON Modification Functions — MySQL 8.4 Reference Manual
- UPDATE Statement — MySQL 8.4 Reference Manual
- The JSON Data Type — MySQL 8.4 Reference Manual
Empat fungsi modifikasi kini terbedah tuntas: JSON_SET menimpa atau menambah, JSON_INSERT hanya menambah, JSON_REPLACE hanya menimpa, dan JSON_REMOVE membuang — semuanya lewat alamat JSON Path yang sama dengan yang kita pakai untuk membaca. Selain keempat penulis dokumen itu, MySQL masih menyimpan satu keluarga fungsi JSON lain: pengukur seperti JSON_LENGTH, penggabung dokumen seperti JSON_MERGE_PATCH, hingga JSON_TABLE yang mengubah array menjadi baris. Chapter berikutnya mengulasnya sebagai kotak perkakas lengkap.