Ilustrasi database dengan empat simbol aksi tambah, cari, ubah, dan hapus

Di episode sebelumnya kita sudah berkenalan dengan konsep database relasional: tabel, baris, kolom, primary key, dan foreign key. Pengetahuan itu penting, tapi ia masih teori. Sekarang saatnya mempraktikkannya, karena database yang tidak bisa diisi, dibaca, diubah, dan dihapus datanya hanyalah pajangan.

Empat operasi itulah yang disebut CRUD: Create (membuat data), Read (membaca data), Update (mengubah data), dan Delete (menghapus data). Hampir semua fitur aplikasi yang pernah kamu pakai adalah kombinasi dari empat operasi ini. Posting status di media sosial? Create. Membuka beranda? Read. Mengedit profil? Update. Menghapus foto? Delete.

Di dapur Kompor Kode, CRUD itu seperti empat gerakan dasar memasak: memasukkan bahan ke kulkas (create), mengecek isi kulkas (read), mengganti bahan yang salah label (update), dan membuang bahan yang basi (delete). Koki mana pun wajib menguasai empat gerakan ini sebelum berkreasi. Programmer pun begitu.

Di akhir artikel ini kamu akan bisa menulis keempat perintah SQL-nya dengan percaya diri, plus satu kebiasaan penting yang membedakan pemula ceroboh dengan programmer yang amanah menjaga data.

Kenapa CRUD Disebut Tulang Punggung Aplikasi

Ambil contoh proyek buku tamu dari seri PHP Native. Fitur-fiturnya kalau dibedah satu per satu: form pengisian tamu (create), halaman daftar tamu (read), tombol edit komentar (update), tombol hapus (delete). Tidak ada fitur kelima. Aplikasi kasir, aplikasi kas kecil warung, sampai sistem akademik kampus, semuanya dibangun dari empat operasi yang sama, hanya datanya yang berbeda.

Itu sebabnya CRUD adalah titik temu antara teori database dan praktik backend. Episode #63 kemarin memberi peta, episode ini memberi kendaraan. Setelah ini, di #65 kamu akan belajar memfilter data dengan WHERE, di #66 menggabungkan tabel dengan JOIN, dan di #67 mendesain database yang rapi. Semuanya butuh fondasi CRUD yang kuat.

Semua contoh di artikel ini memakai MySQL, database relasional paling populer untuk belajar. Kalau kamu mengikuti seri PHP Native, MySQL sudah terpasang di laptopmu. Buka terminal MySQL dan praktikkan langsung setiap contohnya.

Create: Menambah Data dengan INSERT

Bayangkan kamu punya tabel menu_warung berisi daftar menu sebuah warung makan. Strukturnya sederhana:

CREATE TABLE menu_warung (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(100) NOT NULL,
  harga INT NOT NULL,
  kategori VARCHAR(50)
);

Untuk menambah satu menu baru, perintahnya seperti ini:

INSERT INTO menu_warung (nama, harga, kategori)
VALUES ('Nasi Goreng', 15000, 'Makanan');

Perhatikan polanya: sebutkan nama tabel, lalu daftar kolom dalam kurung, lalu kata VALUES, lalu daftar nilai dalam kurung dengan urutan yang sama persis. Kolom id tidak perlu ditulis karena ia terisi otomatis (AUTO_INCREMENT).

Kenapa nama kolom ditulis eksplisit, padahal bisa saja langsung VALUES (...) tanpa menyebut kolom? Karena tabel bisa berubah. Besok pemilik warung menambah kolom stok, query tanpa nama kolom akan error atau mengisi kolom yang salah. Menulis nama kolom adalah kebiasaan programmer yang berpikir jangka panjang, bukan yang penting jalan hari ini.

Kamu juga bisa memasukkan banyak baris sekaligus dalam satu perintah, cukup pisahkan tiap baris dengan koma. Ini jauh lebih cepat daripada menjalankan INSERT berkali-kali:

INSERT INTO menu_warung (nama, harga, kategori) VALUES
  ('Mie Goreng', 13000, 'Makanan'),
  ('Es Teh', 5000, 'Minuman'),
  ('Kopi Tubruk', 8000, 'Minuman');

Berhenti dan tebak dulu! Apa isi kolom kategori kalau kita menjalankan query ini?

INSERT INTO menu_warung (nama, harga)
VALUES ('Pisang Goreng', 10000);
Jawaban: NULL (kosong). Karena kolom kategori tidak disebut dan tidak punya nilai default, MySQL mengisinya dengan NULL. Kolom nama dan harga wajib diisi karena ditandai NOT NULL, sedangkan kategori boleh kosong. Inilah alasan kenapa desain tabel menentukan perilaku INSERT: aturan NOT NULL adalah kontrak antara kamu dan database.

Read: Membaca Data dengan SELECT

Membaca data adalah operasi yang paling sering kamu lakukan, mungkin 80 persen dari semua query-mu nanti. Bentuk paling sederhananya:

SELECT nama, harga FROM menu_warung;

Artinya: ambil kolom nama dan harga dari tabel menu_warung. Hasilnya adalah daftar semua menu beserta harganya. Analogi dapurnya: seperti daftar belanja, kamu hanya menulis bahan yang dibutuhkan, bukan seluruh isi dapur.

Kamu juga akan sering melihat SELECT *, yang artinya ambil semua kolom. Untuk latihan dan eksplorasi, itu tidak masalah. Tapi ada satu persepsi yang perlu diluruskan sejak sekarang.

Mitos: SELECT * boleh dipakai di mana saja. Faktanya, di aplikasi nyata, SELECT * adalah kebiasaan yang sebaiknya dihindari. Alasannya masuk akal kalau dipikir: kenapa meminta semua kolom kalau yang dibutuhkan cuma dua? Tabel pengguna misalnya, bisa berisi kolom password_hash yang tidak boleh bocor ke halaman web. Memilih kolom secara eksplisit membuat query lebih cepat, lebih hemat memori, dan lebih aman. Anggap SELECT * seperti membawa seluruh isi kulkas saat kamu cuma butuh telur: bisa, tapi merepotkan dan berisiko.

Di episode #65 nanti kamu akan belajar menyaring baris dengan WHERE, supaya tidak selalu mengambil seluruh isi tabel.

Update: Mengubah Data dengan UPDATE

Harga nasi goreng naik jadi 17000. Perintahnya:

UPDATE menu_warung SET harga = 17000 WHERE nama = 'Nasi Goreng';

Bagian WHERE di sini bukan hiasan, ia adalah bagian terpenting dari query ini. Ia menjawab pertanyaan: baris mana yang boleh diubah? Tanpa WHERE, MySQL akan mengubah semua baris di tabel. Semua menu jadi seharga 17000. Bayangkan itu terjadi di database toko online sungguhan.

Berhenti dan tebak dulu! Apa yang terjadi kalau query ini dijalankan di tabel berisi 50 menu?

UPDATE menu_warung SET harga = 10000;
Jawaban: semua 50 menu berubah harganya jadi 10000. Tanpa WHERE, UPDATE tidak tahu baris mana yang kamu maksud, jadi ia mengubah semuanya. Ini kesalahan klasik yang pernah dialami hampir semua programmer backend setidaknya sekali. Makanya aturan emasnya: tidak ada UPDATE tanpa WHERE, kecuali kamu memang sengaja ingin mengubah seluruh tabel dan sudah sadar risikonya.

Programmer berpengalaman punya kebiasaan yang lahir dari pengalaman pahit: coba SELECT dulu sebelum UPDATE atau DELETE. Tulis dulu SELECT * FROM menu_warung WHERE nama = 'Nasi Goreng';, lihat hasilnya, pastikan baris yang muncul memang yang ingin diubah, baru jalankan UPDATE dengan WHERE yang sama. Kebiasaan lima detik ini menyelamatkan banyak karier.

MySQL Workbench bahkan punya mode pengaman (safe update mode) yang menolak UPDATE dan DELETE tanpa WHERE yang memakai kolom kunci, lengkap dengan pesan error 1175. Itu bukan bug yang harus dimatikan, itu sabuk pengaman. Kalau kamu menemukannya, biarkan aktif.

Delete: Menghapus Data dengan DELETE

Menu yang sudah tidak dijual lagi perlu dihapus dari daftar:

DELETE FROM menu_warung WHERE nama = 'Es Teh Manis Versi Lama';

Aturan mainnya sama persis dengan UPDATE: tanpa WHERE, semua baris ikut terhapus. Tidak ada tempat sampah, tidak ada tombol undo. Sekali DELETE FROM menu_warung; tanpa WHERE dijalankan, seluruh isi tabel hilang.

Itulah kenapa aplikasi-aplikasi besar jarang benar-benar menghapus data. Mereka memakai teknik soft delete: menambah kolom seperti is_deleted atau deleted_at, dan "menghapus" artinya mengubah kolom itu, bukan membuang barisnya. Data yang terhapus masih bisa dikembalikan, dan riwayatnya tetap ada. Ini juga meluruskan mitos kedua.

Mitos: data yang terhapus pasti bisa dikembalikan. Hanya benar kalau aplikasinya dirancang begitu (soft delete plus backup rutin). Kalau query DELETE sudah mengenai database tanpa backup, datanya hilang permanen. Programmer yang baik memperlakukan DELETE seperti membuang bahan makanan ke tempat sampah di dapur umum: pastikan dulu, karena tidak ada yang bisa mengambilnya kembali.

Kuis Cepat

Kuis cepat: Pemilik warung minta harga menu dengan id 3 dinaikkan jadi 20000. Query mana yang benar dan aman?

Latihan: Bangun Tabel Buku Tamu Mini

Teori tanpa praktik cepat menguap. Kerjakan latihan ini di MySQL-mu, semuanya memakai perintah dari artikel ini:

  1. Buat tabel buku_tamu dengan kolom id (INT AUTO_INCREMENT PRIMARY KEY), nama (VARCHAR 100), dan pesan (TEXT).
  2. Ubah, jangan ketik ulang: isi tabel dengan 5 baris memakai INSERT, tapi buat satu baris di antaranya memakai INSERT yang hanya mengisi kolom nama saja. Perhatikan apa yang terjadi pada kolom pesan, lalu ubah INSERT-mu supaya mengisi pesan juga.
  3. Baca kembali dengan SELECT, tapi hanya kolom nama. Bandingkan dengan SELECT * dan rasakan bedanya.
  4. Ubah pesan salah satu tamu dengan UPDATE ber-WHERE. Sebelum menjalankannya, tulis dulu versi SELECT-nya untuk memastikan sasarannya benar.
  5. Hapus satu baris dengan DELETE ber-WHERE.
  6. Tantangan: tambahkan kolom is_deleted (TINYINT DEFAULT 0) ke tabelmu, lalu praktikkan soft delete: "hapus" satu tamu dengan UPDATE, dan tampilkan hanya tamu yang belum dihapus dengan SELECT ber-WHERE.

Kalau semua langkah di atas berjalan tanpa error, fondasi CRUD-mu sudah kokoh. Di episode #65, Filter Data dengan Where, Order By, dan Limit, kita naik satu tingkat: menyaring, mengurutkan, dan membatasi hasil query. Tiga kemampuan yang akan kamu pakai di hampir setiap halaman aplikasi web yang kamu buat nanti.