Category: Database

  • Belajar Database dari Nol #5: INSERT, Menambah Data ke Tabel

    Belajar Database dari Nol #5: INSERT, Menambah Data ke Tabel

    Cara paling dasar untuk insert data MySQL adalah perintah INSERT INTO nama_tabel (kolom1, kolom2) VALUES (nilai1, nilai2);. Satu perintah bisa memasukkan satu baris, bisa juga puluhan baris sekaligus dengan memisahkan tiap baris pakai koma. Di MySQL 8.4, perintah ini juga yang paling sering dipakai di dunia kerja, baik lewat terminal langsung maupun lewat kode aplikasi.

    Artikel ini bagian kelima dari seri Belajar Database dari Nol. Di bagian ini kita tidak cuma belajar sintaks. Kita sekalian menyiapkan dataset studi kasus toko online berisi tabel produk, pelanggan, dan pesanan. Dataset ini akan dipakai terus sampai akhir seri, jadi kerjakan bagian ini sampai selesai supaya bagian berikutnya tinggal jalan.

    Prasyarat: MySQL 8.4 dan Database Kosong

    Kamu butuh MySQL Server 8.4 yang sudah jalan dan bisa login lewat terminal. Kalau belum, mundur dulu ke bagian pertama seri untuk instalasi. Kamu juga sebaiknya sudah paham kenapa kolom harga pakai DECIMAL dan bukan FLOAT. Itu dibahas tuntas di Belajar Database dari Nol #4: Tipe Data MySQL yang Tepat.

    Supaya semua pembaca mulai dari titik yang sama, kita buat database baru khusus studi kasus. Login dulu ke MySQL, lalu jalankan ini:

    CREATE DATABASE IF NOT EXISTS toko_online;
    USE toko_online;

    Sekarang buat tiga tabel utamanya. Relasi antar tabel dengan foreign key baru kita pasang di bagian 11, jadi untuk sekarang kolom pelanggan_id dan produk_id di tabel pesanan masih berupa angka biasa:

    CREATE TABLE produk (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      nama VARCHAR(100) NOT NULL,
      kategori VARCHAR(50) NOT NULL,
      harga DECIMAL(12,2) NOT NULL,
      stok INT UNSIGNED NOT NULL DEFAULT 0
    );
    
    CREATE TABLE pelanggan (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      nama VARCHAR(100) NOT NULL,
      email VARCHAR(100) NOT NULL UNIQUE,
      kota VARCHAR(50),
      terdaftar_pada DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
    );
    
    CREATE TABLE pesanan (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      pelanggan_id INT UNSIGNED NOT NULL,
      produk_id INT UNSIGNED NOT NULL,
      jumlah INT UNSIGNED NOT NULL DEFAULT 1,
      total DECIMAL(12,2) NOT NULL,
      status VARCHAR(20) NOT NULL DEFAULT 'pending',
      tanggal_pesan DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
    );

    Kalau ketiga perintah sukses, tiap CREATE TABLE mengembalikan Query OK, 0 rows affected. Cek dengan SHOW TABLES; dan pastikan muncul tiga tabel: pelanggan, pesanan, produk.

    INSERT INTO Satu Baris: Bentuk Paling Dasar

    Kita mulai dari satu baris. Masukkan satu produk ke tabel produk:

    INSERT INTO produk (nama, kategori, harga, stok)
    VALUES ('Kaos Polos Hitam', 'Pakaian', 55000, 120);

    Output yang diharapkan:

    Query OK, 1 row affected (0.01 sec)

    Perhatikan strukturnya. Setelah nama tabel ada daftar kolom dalam kurung, lalu VALUES berisi nilai dengan urutan yang sama persis. Nilai teks diapit tanda kutip satu, angka ditulis polos tanpa kutip.

    Satu jebakan kecil soal tanda kutip. Kalau nilai teksnya sendiri mengandung tanda kutip satu, misalnya nama produk Kaos Sablon 'Limited', kamu harus menuliskannya dua kali berturut-turut: 'Kaos Sablon ''Limited'''. Alternatifnya pakai garis miring terbalik, 'Kaos Sablon \'Limited\''. Kalau lupa, MySQL menganggap string sudah selesai di kutip pertama dan sisanya dianggap sintaks rusak.

    Sebenarnya MySQL mengizinkan kamu melewatkan daftar kolom, seperti INSERT INTO produk VALUES (...);. Jangan dibiasakan. Tanpa daftar kolom, kamu wajib mengisi semua kolom sesuai urutan di tabel, termasuk kolom id. Begitu struktur tabel berubah, misalnya ada kolom baru, query lama langsung error. Menyebut kolom secara eksplisit membuat query tetap jalan dan lebih mudah dibaca orang lain.

    INSERT Multi Baris Sekaligus, Lebih Cepat dan Lebih Rapi

    Kalau mau memasukkan lima produk, kamu tidak perlu menulis lima perintah. Cukup satu INSERT dengan beberapa kelompok VALUES yang dipisah koma:

    INSERT INTO produk (nama, kategori, harga, stok) VALUES
    ('Kemeja Flanel Kotak', 'Pakaian', 145000, 45),
    ('Sepatu Lari Ringan', 'Sepatu', 320000, 30),
    ('Tas Ransel Laptop 15 inci', 'Tas', 210000, 25),
    ('Topi Baseball Navy', 'Aksesoris', 48000, 80),
    ('Jaket Hoodie Abu', 'Pakaian', 175000, 60);

    Output yang diharapkan:

    Query OK, 5 rows affected (0.01 sec)
    Records: 5  Duplicates: 0  Warnings: 0

    Selain lebih ringkas, cara ini jauh lebih cepat karena server hanya memproses satu perintah untuk banyak baris. Di proyek klien, tim Arrazy memakai pola multi-baris seperti ini untuk seeder, yaitu skrip pengisi data awal, baik lewat migration Laravel maupun program seeding di backend Go. Prinsipnya sama persis dengan yang kamu tulis manual di sini.

    Perilaku AUTO_INCREMENT, NULL, dan DEFAULT Saat Kolom Tidak Diisi

    Kamu mungkin sadar dari tadi kita tidak pernah mengisi kolom id. Itu bukan kelalaian. Ada tiga mekanisme yang bekerja diam-diam saat sebuah kolom tidak disebut di perintah INSERT:

    Mekanisme Kapan aktif Nilai yang diisi
    AUTO_INCREMENT Kolom bertanda AUTO_INCREMENT tidak diisi Angka berikutnya, mulai dari 1 lalu naik terus
    DEFAULT Kolom punya klausa DEFAULT Nilai default yang ditulis saat membuat tabel
    NULL Kolom boleh kosong dan tanpa default NULL, alias tidak ada nilai

    Coba buktikan dengan tabel pelanggan. Masukkan satu pelanggan tanpa menyebut id, kota, dan terdaftar_pada:

    INSERT INTO pelanggan (nama, email)
    VALUES ('Budi Santoso', 'budi@example.com');
    
    SELECT * FROM pelanggan;

    Hasilnya kira-kira begini:

    +----+--------------+------------------+------+---------------------+
    | id | nama         | email            | kota | terdaftar_pada      |
    +----+--------------+------------------+------+---------------------+
    |  1 | Budi Santoso | budi@example.com | NULL | 2026-07-27 09:15:42 |
    +----+--------------+------------------+------+---------------------+

    Tiga hal terjadi sekaligus. Kolom id terisi 1 dari AUTO_INCREMENT. Kolom kota jadi NULL karena boleh kosong dan tidak punya default. Kolom terdaftar_pada terisi waktu saat ini karena defaultnya CURRENT_TIMESTAMP.

    Kalau kamu butuh id yang baru saja dibuat, misalnya untuk dipakai di tabel lain, panggil SELECT LAST_INSERT_ID(); tepat setelah insert. Untuk memaksa kolom memakai nilai defaultnya secara eksplisit, tulis kata kunci DEFAULT di posisi nilainya, contohnya VALUES ('Sandal Jepit', 'Sepatu', 25000, DEFAULT) yang membuat stok terisi 0.

    Satu catatan penting soal NULL. NULL bukan nol dan bukan string kosong. NULL artinya nilainya memang tidak diketahui. Kolom yang ditandai NOT NULL menolak nilai ini, dan itu bagus untuk data yang wajib ada seperti nama produk atau harga.

    Mengisi Dataset Studi Kasus Toko Online

    Sekarang kita lengkapi dataset yang akan dipakai sepanjang seri. Tabel produk sudah berisi 6 baris. Tambahkan pelanggan dan pesanan berikut apa adanya, jangan diubah dulu, supaya hasil query kamu di bagian-bagian berikutnya sama dengan contoh di artikel:

    INSERT INTO pelanggan (nama, email, kota) VALUES
    ('Siti Rahayu', 'siti@example.com', 'Semarang'),
    ('Agus Wijaya', 'agus@example.com', 'Purwokerto'),
    ('Dewi Lestari', 'dewi@example.com', 'Banjarnegara'),
    ('Rudi Hartono', 'rudi@example.com', NULL);
    
    INSERT INTO pesanan (pelanggan_id, produk_id, jumlah, total, status) VALUES
    (1, 1, 2, 110000, 'selesai'),
    (1, 4, 1, 48000, 'selesai'),
    (2, 3, 1, 320000, 'dikirim'),
    (3, 6, 1, 175000, 'pending'),
    (4, 2, 2, 290000, 'selesai'),
    (5, 5, 3, 630000, 'dibatalkan');

    Perhatikan baris Rudi Hartono. Kita sengaja menulis NULL secara eksplisit di kolom kota. Ini sah karena kolom kota memang boleh kosong, dan nanti berguna saat belajar filter IS NULL. Di tabel pesanan, kolom tanggal_pesan kita biarkan terisi otomatis oleh default CURRENT_TIMESTAMP.

    Total dataset kamu sekarang: 6 produk, 5 pelanggan, dan 6 pesanan. Struktur toko online sederhana seperti ini juga jadi fondasi sistem yang lebih besar. Pola tabel produk, pelanggan, dan pesanan yang sama kami pakai saat membangun sistem aplikasi untuk klien, tentu dengan kolom dan relasi yang jauh lebih banyak.

    Cek Hasil dengan SELECT * Sebelum Lanjut

    Biasakan langsung memeriksa hasil setiap selesai memasukkan data. Cara tercepatnya adalah SELECT *, yang artinya tampilkan semua kolom:

    SELECT * FROM produk;
    SELECT * FROM pelanggan;
    SELECT * FROM pesanan;

    Untuk tabel produk, hasilnya harus 6 baris dengan id berurutan 1 sampai 6:

    +----+---------------------------+-----------+-----------+------+
    | id | nama                      | kategori  | harga     | stok |
    +----+---------------------------+-----------+-----------+------+
    |  1 | Kaos Polos Hitam          | Pakaian   |  55000.00 |  120 |
    |  2 | Kemeja Flanel Kotak       | Pakaian   | 145000.00 |   45 |
    |  3 | Sepatu Lari Ringan        | Sepatu    | 320000.00 |   30 |
    |  4 | Tas Ransel Laptop 15 inci | Tas       | 210000.00 |   25 |
    |  5 | Topi Baseball Navy        | Aksesoris |  48000.00 |   80 |
    |  6 | Jaket Hoodie Abu          | Pakaian   | 175000.00 |   60 |
    +----+---------------------------+-----------+-----------+------+
    6 rows in set (0.00 sec)

    Kalau jumlah barisnya tidak cocok, hapus isi tabel dengan TRUNCATE TABLE nama_tabel; lalu ulangi insert dari awal. TRUNCATE mengosongkan tabel sekaligus mereset hitungan AUTO_INCREMENT kembali ke 1, jadi id kamu kembali rapi. Untuk sekarang cukup SELECT * saja dulu. Menyaring baris tertentu dengan kondisi adalah materi bagian berikutnya.

    Kebiasaan cek cepat seperti ini terlihat sepele, tapi menyelamatkan banyak waktu. Salah ketik satu angka di kolom total jauh lebih murah ditemukan sekarang, saat datanya masih 6 baris, daripada nanti saat kamu bingung kenapa hasil agregasi di bagian 10 tidak masuk akal. Data yang salah sejak insert akan salah terus di semua query di atasnya.

    Troubleshooting: Error INSERT yang Paling Sering Muncul

    ERROR 1062: Duplicate entry for key PRIMARY

    ERROR 1062 (23000): Duplicate entry '1' for key 'produk.PRIMARY'

    Penyebab: kamu memasukkan nilai id yang sudah dipakai baris lain, biasanya karena mengisi kolom AUTO_INCREMENT secara manual. Primary key harus unik, jadi MySQL menolak. Error yang sama muncul di kolom UNIQUE, misalnya memasukkan email pelanggan yang sudah terdaftar. Solusi: jangan isi kolom id sama sekali, biarkan AUTO_INCREMENT bekerja. Untuk email duplikat, cek dulu datanya atau pakai email lain.

    ERROR 1406: Data too long for column

    ERROR 1406 (22001): Data too long for column 'status' at row 1

    Penyebab: nilai yang dimasukkan melebihi kapasitas kolom. Kolom status kita bertipe VARCHAR(20), jadi teks 21 karakter atau lebih ditolak. Di MySQL 8.4 dengan pengaturan bawaan (strict mode aktif), data tidak dipotong diam-diam tapi langsung ditolak, dan itu perilaku yang benar. Solusi: perpendek nilainya, atau kalau kebutuhan datanya memang lebih panjang, ubah kolom dengan ALTER TABLE pesanan MODIFY status VARCHAR(50) NOT NULL DEFAULT 'pending';.

    ERROR 1364: Field doesn’t have a default value

    ERROR 1364 (HY000): Field 'harga' doesn't have a default value

    Penyebab: ada kolom NOT NULL tanpa default yang tidak kamu sebut di daftar kolom. MySQL tidak tahu harus mengisi apa, jadi menolak. Solusi: sertakan kolom itu di perintah insert beserta nilainya. Ini alasan lain kenapa daftar kolom eksplisit itu penting, kamu langsung tahu kolom mana yang wajib diisi.

    ERROR 1136: Column count doesn’t match value count

    ERROR 1136 (21S01): Column count doesn't match value count at row 1

    Penyebab: jumlah nilai di VALUES tidak sama dengan jumlah kolom yang disebut. Biasanya gara-gara lupa satu nilai atau kelebihan koma saat menulis insert multi-baris yang panjang. Solusi: hitung ulang pasangan kolom dan nilai. Pesan errornya menyebut nomor baris yang bermasalah, mulai cek dari situ.

    Lanjut ke Bagian Berikutnya

    Dataset toko online kamu sekarang sudah terisi dan siap dipakai. Kamu sudah bisa insert data MySQL satu baris maupun banyak baris, paham apa yang terjadi pada kolom yang tidak diisi, dan tahu cara membaca error yang paling sering muncul.

    Data yang cuma bisa dimasukkan tapi tidak bisa dicari tentu belum berguna. Di bagian berikutnya, Belajar Database dari Nol #6: SELECT & WHERE, Query Dasar, kita mulai menggali data ini dengan query yang sesungguhnya, dari memilih kolom tertentu sampai menyaring baris dengan berbagai kondisi. Artikelnya terbit menyusul, pantau daftar lengkapnya di halaman hub Belajar Database.

    Referensi

  • Belajar Database dari Nol #4: Tipe Data MySQL yang Tepat

    Belajar Database dari Nol #4: Tipe Data MySQL yang Tepat

    Memilih tipe data MySQL yang tepat itu sederhana kalau pegang empat aturan dasar. Bilangan bulat pakai INT, uang pakai DECIMAL dan jangan pernah FLOAT, teks pendek pakai VARCHAR, lalu tanggal dan waktu pakai DATE atau DATETIME. Sisanya adalah soal memahami kapan aturan dasar itu perlu disesuaikan, dan itulah isi tutorial ini.

    Ini bagian keempat dari seri Belajar Database dari Nol. Di bagian sebelumnya kita sudah membuat database dan tabel pertama. Sekarang kita bedah kolom demi kolom: kenapa tipe data yang salah bisa bikin saldo pelanggan meleset, teks terpotong, atau jam transaksi bergeser tujuh jam.

    Prasyarat Sebelum Mulai

    Tutorial ini memakai MySQL 8.4 yang sudah kamu install di bagian pertama seri. Kamu juga perlu paham cara membuat database dan tabel. Kalau belum, selesaikan dulu Belajar Database dari Nol #3: Membuat Database & Tabel MySQL karena semua contoh di sini dibangun dari perintah CREATE TABLE.

    Siapkan database latihan supaya percobaan kita tidak mengganggu tabel lain:

    CREATE DATABASE IF NOT EXISTS latihan_tipe;
    USE latihan_tipe;

    Tipe Data Angka: INT, BIGINT, dan DECIMAL

    MySQL punya beberapa tipe bilangan bulat. Bedanya cuma satu: seberapa besar angka yang bisa ditampung, dan berapa byte yang dipakai per baris.

    Tipe Ukuran Rentang (signed) Rentang (unsigned)
    TINYINT 1 byte -128 s.d. 127 0 s.d. 255
    SMALLINT 2 byte -32.768 s.d. 32.767 0 s.d. 65.535
    INT 4 byte -2.147.483.648 s.d. 2.147.483.647 0 s.d. 4.294.967.295
    BIGINT 8 byte kira-kira -9,2 kuintiliun s.d. 9,2 kuintiliun 0 s.d. 18,4 kuintiliun

    Aturan praktisnya: INT cukup untuk hampir semua kebutuhan, termasuk primary key tabel yang isinya jutaan baris. BIGINT baru dibutuhkan kalau kamu yakin barisnya bakal melewati 2,1 miliar, misalnya tabel log atau tabel transaksi sistem besar. Jangan pakai BIGINT untuk semua kolom hanya karena “biar aman”, karena setiap baris jadi lebih boros 4 byte per kolom dan index ikut membengkak.

    Tambahkan UNSIGNED kalau nilainya tidak mungkin negatif, misalnya stok atau id. Rentang positifnya jadi dua kali lipat:

    CREATE TABLE produk (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      nama VARCHAR(100) NOT NULL,
      stok SMALLINT UNSIGNED NOT NULL DEFAULT 0
    );

    Kenapa Uang Jangan Pakai FLOAT atau DOUBLE

    FLOAT dan DOUBLE menyimpan angka secara biner dengan presisi terbatas. Banyak pecahan desimal seperti 0,1 tidak bisa diwakili secara persis dalam biner, jadi yang tersimpan adalah nilai pendekatan. Buktikan sendiri:

    CREATE TABLE demo_float (saldo FLOAT);
    INSERT INTO demo_float VALUES (0.1), (0.2);
    SELECT SUM(saldo) FROM demo_float;

    Hasilnya bukan 0,3:

    +---------------------+
    | SUM(saldo)          |
    +---------------------+
    | 0.30000000447034836 |
    +---------------------+

    Selisihnya kelihatan kecil, tapi di sistem keuangan selisih sekecil apa pun itu masalah. Total invoice bisa tidak cocok dengan rincian, dan pembulatan yang menumpuk bikin laporan akuntansi tidak balance. Solusinya DECIMAL, yang menyimpan angka secara eksak sesuai digit yang kamu tentukan:

    CREATE TABLE demo_decimal (saldo DECIMAL(15,2));
    INSERT INTO demo_decimal VALUES (0.1), (0.2);
    SELECT SUM(saldo) FROM demo_decimal;
    +------------+
    | SUM(saldo) |
    +------------+
    |       0.30 |
    +------------+

    DECIMAL(15,2) artinya total 15 digit, 2 di antaranya di belakang koma. Untuk rupiah, DECIMAL(15,2) sudah menampung sampai ratusan triliun. Di proyek klien yang tim Arrazy kerjakan, semua kolom harga, saldo, dan total transaksi pada sistem aplikasi yang kami bangun selalu memakai DECIMAL, tanpa pengecualian. FLOAT dan DOUBLE hanya layak untuk data ilmiah atau pengukuran yang memang toleran terhadap pendekatan, misalnya koordinat atau hasil sensor.

    Tipe Data Teks: VARCHAR, CHAR, dan TEXT

    Tiga tipe ini sering ketukar. Bedanya ada di cara penyimpanan:

    • VARCHAR(n) menyimpan teks dengan panjang bervariasi sampai maksimal n karakter. Kata “Budi” di kolom VARCHAR(100) hanya memakai ruang sebesar 4 karakter plus 1 byte penanda panjang. Ini pilihan default untuk hampir semua teks: nama, email, judul, alamat.
    • CHAR(n) selalu memakai ruang tetap n karakter, sisa ruangnya diisi spasi. Cocok hanya untuk data yang panjangnya benar-benar seragam, misalnya kode provinsi 2 huruf atau kode mata uang seperti IDR dan USD.
    • TEXT untuk teks panjang yang tidak jelas batasnya, misalnya isi artikel atau deskripsi produk. TEXT disimpan terpisah dari baris utama, tidak bisa punya nilai DEFAULT, dan kalau mau diindex harus pakai prefix index. Jadi jangan pakai TEXT untuk kolom yang sebenarnya pendek.

    Soal memilih panjang VARCHAR, pakai angka yang wajar sesuai data aslinya. VARCHAR(100) untuk nama orang, VARCHAR(255) untuk email atau URL, VARCHAR(20) untuk nomor telepon. Nomor telepon disimpan sebagai teks, bukan angka, karena ada nol di depan dan kadang tanda plus.

    Angka panjang di VARCHAR tidak bikin baris lebih besar selama isinya pendek, tapi tetap jangan asal tulis VARCHAR(5000). MySQL memakai panjang deklarasi saat membuat tabel sementara di memori untuk operasi tertentu, dan batas satu baris InnoDB juga terbatas sekitar 65.535 byte untuk semua kolom non TEXT. Deklarasi yang jujur juga berfungsi sebagai validasi gratis: kolom kode pos VARCHAR(10) otomatis menolak input yang jelas salah.

    CREATE TABLE pelanggan (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      nama VARCHAR(100) NOT NULL,
      email VARCHAR(255) NOT NULL,
      telepon VARCHAR(20),
      kode_negara CHAR(2) NOT NULL DEFAULT 'ID',
      catatan TEXT
    );

    Tipe Data Tanggal dan Waktu: DATE, DATETIME, TIMESTAMP

    Tiga tipe utama untuk waktu, dengan fungsi berbeda:

    • DATE hanya tanggal, format YYYY-MM-DD. Pas untuk tanggal lahir atau tanggal jatuh tempo.
    • DATETIME tanggal plus jam, rentang tahun 1000 sampai 9999. Nilainya disimpan apa adanya, tidak peduli timezone server.
    • TIMESTAMP tanggal plus jam juga, tapi rentangnya terbatas dari tahun 1970 sampai awal 2038. Nilainya dikonversi ke UTC saat disimpan, lalu dikonversi balik ke timezone sesi saat dibaca.

    Perbedaan Perilaku Timezone DATETIME vs TIMESTAMP

    Ini perbedaan paling penting dan paling sering bikin bingung. Jalankan percobaan ini:

    CREATE TABLE demo_waktu (
      pakai_datetime DATETIME,
      pakai_timestamp TIMESTAMP
    );
    
    SET time_zone = '+07:00';
    INSERT INTO demo_waktu VALUES ('2026-07-27 10:00:00', '2026-07-27 10:00:00');
    
    SET time_zone = '+00:00';
    SELECT * FROM demo_waktu;

    Hasilnya:

    +---------------------+---------------------+
    | pakai_datetime      | pakai_timestamp     |
    +---------------------+---------------------+
    | 2026-07-27 10:00:00 | 2026-07-27 03:00:00 |
    +---------------------+---------------------+

    Kolom DATETIME tetap menunjukkan jam 10 pagi karena nilainya disimpan mentah. Kolom TIMESTAMP bergeser jadi jam 3 karena MySQL menyimpannya sebagai UTC lalu menampilkannya sesuai timezone sesi yang sekarang kita ubah ke +00:00. Perilaku ini berguna kalau aplikasimu punya pengguna lintas timezone, tapi bisa mengejutkan kalau kamu tidak sadar.

    Rekomendasi praktis untuk aplikasi Indonesia yang penggunanya satu timezone: pakai DATETIME untuk data bisnis seperti jadwal dan tanggal transaksi, lalu pakai TIMESTAMP untuk kolom audit created_at dan updated_at. Untuk kolom audit, MySQL bisa mengisinya otomatis:

    CREATE TABLE pesanan (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      total DECIMAL(15,2) NOT NULL,
      tanggal_kirim DATE,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    );

    Satu catatan lagi: TIMESTAMP mentok di 19 Januari 2038. Untuk kolom yang mungkin berisi tanggal jauh di masa depan, misalnya masa berlaku kontrak, pakai DATETIME.

    ENUM, BOOLEAN, dan Kapan Pakai Tabel Referensi

    ENUM membatasi isi kolom pada daftar nilai yang kamu tetapkan saat membuat tabel:

    CREATE TABLE pesanan_status (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      status ENUM('menunggu', 'dibayar', 'dikirim', 'selesai') NOT NULL DEFAULT 'menunggu'
    );

    Nilai di luar daftar akan ditolak, jadi datamu terjaga. Kelemahannya, menambah status baru berarti harus ALTER TABLE, dan itu operasi yang berat di tabel besar. ENUM juga diurutkan berdasarkan posisi di daftar, bukan alfabet, yang kadang bikin ORDER BY terasa aneh.

    MySQL tidak punya tipe BOOLEAN sungguhan. Saat kamu menulis BOOLEAN atau BOOL, MySQL diam-diam membuatnya sebagai TINYINT(1), dengan 0 dianggap false dan selain 0 dianggap true:

    CREATE TABLE pengguna (
      id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      aktif BOOLEAN NOT NULL DEFAULT TRUE
    );
    
    SHOW CREATE TABLE pengguna\G

    Di output SHOW CREATE TABLE kamu akan melihat kolom aktif tertulis sebagai tinyint(1). Tidak masalah, itu memang cara MySQL. Yang penting konsisten: simpan hanya 0 dan 1.

    Lalu kapan sebaiknya pakai tabel referensi ketimbang ENUM? Pakai tabel referensi kalau daftarnya berpotensi bertambah lewat aplikasi, perlu menyimpan info tambahan per nilai, atau perlu diubah admin tanpa menyentuh struktur database. Contoh: kategori produk jelas lebih cocok jadi tabel sendiri yang nanti dihubungkan lewat foreign key, materi yang kita bahas tuntas di bagian 11 seri ini. ENUM cukup untuk daftar yang benar-benar stabil seperti status pesanan atau jenis kelamin.

    Troubleshooting Error Tipe Data yang Sering Muncul

    MySQL 8.4 berjalan dalam strict mode secara default, jadi data yang tidak cocok dengan tipe kolom langsung ditolak dengan error, bukan dipotong diam-diam. Ini bagus, tapi berarti kamu akan sering ketemu error berikut saat belajar.

    ERROR 1264: Out of range value for column

    INSERT INTO produk (nama, stok) VALUES ('Tes', 70000);
    ERROR 1264 (22003): Out of range value for column 'stok' at row 1

    Penyebab: nilai melebihi kapasitas tipe. Di contoh ini stok bertipe SMALLINT UNSIGNED yang maksimal 65.535. Solusinya perbesar tipenya, misalnya ALTER TABLE produk MODIFY stok INT UNSIGNED NOT NULL DEFAULT 0;. Error yang sama muncul kalau kamu memasukkan angka negatif ke kolom UNSIGNED.

    ERROR 1265: Data truncated for column

    INSERT INTO pesanan_status (status) VALUES ('batal');
    ERROR 1265 (01000): Data truncated for column 'status' at row 1

    Penyebab paling umum: memasukkan nilai yang tidak ada di daftar ENUM, atau memasukkan teks ke kolom angka. Cek daftar nilai yang sah dengan SHOW COLUMNS FROM pesanan_status; lalu perbaiki nilainya, atau tambahkan nilai baru ke ENUM lewat ALTER TABLE kalau memang dibutuhkan.

    ERROR 1406: Data too long for column

    INSERT INTO pelanggan (nama, email, kode_negara)
    VALUES ('Budi', 'budi@contoh.com', 'IDN');
    ERROR 1406 (22001): Data too long for column 'kode_negara' at row 1

    Penyebab: teks lebih panjang dari deklarasi kolom, di sini CHAR(2) diisi 3 huruf. Kalau datanya yang salah, perbaiki datanya. Kalau deklarasinya yang terlalu sempit, perlebar dengan ALTER TABLE ... MODIFY.

    ERROR 1292: Incorrect datetime value

    INSERT INTO pesanan (total, tanggal_kirim) VALUES (150000, '27-07-2026');
    ERROR 1292 (22007): Incorrect date value: '27-07-2026' for column 'tanggal_kirim' at row 1

    Penyebab: format tanggal salah. MySQL menerima format YYYY-MM-DD, bukan DD-MM-YYYY gaya Indonesia. Tulis '2026-07-27'. Kalau sumber datanya memang berformat lain, konversi dulu dengan fungsi STR_TO_DATE('27-07-2026', '%d-%m-%Y').

    Rangkuman dan Lanjut ke Bagian 5

    Pegangan singkatnya: INT untuk bilangan bulat dan naikkan ke BIGINT hanya kalau perlu, DECIMAL untuk semua nilai uang, VARCHAR dengan panjang wajar untuk teks pendek dan TEXT untuk konten panjang, DATETIME untuk waktu bisnis dan TIMESTAMP untuk kolom audit, lalu ENUM hanya untuk daftar yang stabil. Tipe yang tepat sejak awal jauh lebih murah daripada ALTER TABLE di tabel yang sudah berisi jutaan baris.

    Di bagian berikutnya, Belajar Database dari Nol #5: INSERT, Menambah Data ke Tabel, kita mulai mengisi tabel dengan berbagai variasi perintah INSERT, termasuk memasukkan banyak baris sekaligus. Artikelnya terbit menyusul dan bisa kamu pantau di halaman hub seri Belajar Database.

    Referensi

  • Belajar Database dari Nol #3: Membuat Database & Tabel MySQL

    Belajar Database dari Nol #3: Membuat Database & Tabel MySQL

    Cara membuat database MySQL sebenarnya cuma satu baris: CREATE DATABASE nama_database;. Setelah itu jalankan USE nama_database; supaya semua perintah berikutnya masuk ke database itu, lalu buat tabel pertama dengan CREATE TABLE. Tiga perintah ini adalah fondasi semua pekerjaan database, dan di artikel ini kita praktikkan semuanya sampai kamu punya tabel yang benar-benar bisa diisi data.

    Artikel ini bagian ketiga dari seri Belajar Database dari Nol. Kalau di dua bagian sebelumnya kita masih banyak di konsep, mulai sekarang tangan kamu yang bekerja. Semua contoh di sini kami uji di MySQL 8.4, versi LTS yang sama dengan yang dipakai di bagian pertama seri ini.

    Prasyarat Sebelum Praktik

    Pastikan dua hal ini sudah beres. Pertama, MySQL 8.4 sudah terpasang dan kamu bisa masuk ke klien mysql dari terminal. Kedua, kamu sudah paham istilah dasar seperti tabel, baris, kolom, dan primary key. Kalau istilah itu masih asing, baca dulu Belajar Database dari Nol #2: Konsep Database Relasional karena semua praktik di sini memakai konsep dari sana.

    Masuk ke MySQL sebagai root:

    mysql -u root -p

    Ketik password, dan kamu akan melihat prompt mysql>. Semua perintah SQL di artikel ini dijalankan dari prompt itu.

    Cara Membuat Database MySQL dengan CREATE DATABASE

    Kita akan membangun database untuk studi kasus yang dipakai sepanjang seri ini: toko online sederhana. Jalankan:

    CREATE DATABASE toko_online;

    Output yang diharapkan:

    Query OK, 1 row affected (0.01 sec)

    Selesai. Database toko_online sudah ada. Untuk memastikan, lihat daftar semua database di server:

    SHOW DATABASES;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | toko_online        |
    +--------------------+
    5 rows in set (0.00 sec)

    Empat database selain toko_online adalah bawaan MySQL. Jangan diutak-atik, apalagi dihapus. MySQL menyimpan konfigurasi user, hak akses, dan metadata di sana.

    Di MySQL 8.4, CREATE DATABASE otomatis memakai character set utf8mb4. Ini kabar baik karena utf8mb4 mendukung semua karakter Unicode termasuk emoji, jadi kamu tidak perlu opsi tambahan untuk kasus umum.

    Perintah USE: Memilih Database Aktif

    Membuat database tidak otomatis membuatnya aktif. Kamu harus memilihnya dulu:

    USE toko_online;
    Database changed

    Mulai titik ini, semua perintah CREATE TABLE, SELECT, dan lainnya berjalan di dalam toko_online. Lupa menjalankan USE adalah sumber error nomor satu bagi pemula, dan kita bahas errornya di bagian troubleshooting.

    Konvensi Penamaan: Huruf Kecil dan snake_case

    Nama toko_online bukan pilihan asal. Ada konvensi yang dipakai luas di industri, dan tim kami di Arrazy juga menerapkannya di semua sistem aplikasi yang kami bangun untuk klien:

    • Huruf kecil semua. Di Linux, nama database dan tabel sensitif huruf besar kecil karena dipetakan ke nama folder dan file. Toko_Online dan toko_online dianggap dua database berbeda. Di Windows tidak sensitif. Kalau kamu konsisten pakai huruf kecil, kode kamu aman dipindah antar sistem operasi.
    • Pisahkan kata dengan underscore (snake_case). Tulis toko_online, bukan tokoonline atau tokoOnline. Lebih mudah dibaca dan tidak butuh tanda kutip khusus.
    • Hindari spasi dan karakter aneh. Nama dengan spasi memaksa kamu menulis backtick seperti `toko online` di setiap query. Merepotkan selamanya.
    • Nama harus menjelaskan isinya. toko_online jelas. db1 atau test2 akan membingungkan kamu sendiri tiga bulan lagi.

    CREATE TABLE: Membuat Tabel Pertama

    Sekarang bagian intinya. Kita buat tabel produk untuk menyimpan barang dagangan. Ketik perintah ini apa adanya, termasuk komanya:

    CREATE TABLE produk (
        id INT AUTO_INCREMENT PRIMARY KEY,
        nama VARCHAR(100) NOT NULL,
        harga INT NOT NULL,
        stok INT NOT NULL DEFAULT 0,
        dibuat_pada DATETIME DEFAULT CURRENT_TIMESTAMP
    );
    Query OK, 0 rows affected (0.03 sec)

    Bedah baris per baris, karena setiap kata di sini punya alasan:

    • id INT AUTO_INCREMENT PRIMARY KEY membuat kolom identitas. INT artinya bilangan bulat. AUTO_INCREMENT membuat MySQL mengisi nilainya otomatis: baris pertama dapat 1, berikutnya 2, dan seterusnya, tanpa kamu pikirkan. PRIMARY KEY menjadikannya pengenal unik tiap baris, konsep yang sudah kita bahas di bagian dua. Hampir semua tabel yang kamu buat seumur hidup akan diawali baris seperti ini.
    • nama VARCHAR(100) NOT NULL menyimpan teks maksimal 100 karakter. NOT NULL artinya kolom ini wajib diisi. Produk tanpa nama tidak masuk akal, jadi kita larang dari level database, bukan cuma dari level aplikasi.
    • harga INT NOT NULL menyimpan harga dalam rupiah utuh. Untuk kasus belajar ini INT cukup. Pemilihan tipe data yang lebih serius kita bahas tuntas di bagian empat seri ini.
    • stok INT NOT NULL DEFAULT 0 memperkenalkan DEFAULT. Kalau saat memasukkan data kamu tidak menyebut stok, MySQL mengisinya 0. Nilai default membuat data lebih dapat diprediksi.
    • dibuat_pada DATETIME DEFAULT CURRENT_TIMESTAMP mencatat kapan baris dibuat, otomatis terisi waktu saat itu. Pola ini sangat umum di aplikasi nyata untuk audit sederhana.

    Perhatikan pola komanya: setiap definisi kolom diakhiri koma, kecuali kolom terakhir sebelum tanda tutup kurung. Salah taruh koma adalah error sintaks paling sering di CREATE TABLE, dan kita bahas contoh nyatanya di troubleshooting.

    Membaca Struktur Tabel dengan DESCRIBE dan SHOW CREATE TABLE

    Tabel sudah jadi, tapi bagaimana melihat bentuknya? Ada dua perintah. Yang pertama, DESCRIBE, memberi ringkasan cepat:

    DESCRIBE produk;
    +-------------+--------------+------+-----+-------------------+-------------------+
    | Field       | Type         | Null | Key | Default           | Extra             |
    +-------------+--------------+------+-----+-------------------+-------------------+
    | id          | int          | NO   | PRI | NULL              | auto_increment    |
    | nama        | varchar(100) | NO   |     | NULL              |                   |
    | harga       | int          | NO   |     | NULL              |                   |
    | stok        | int          | NO   |     | 0                 |                   |
    | dibuat_pada | datetime     | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
    +-------------+--------------+------+-----+-------------------+-------------------+
    5 rows in set (0.00 sec)

    Kolom Null menunjukkan mana yang boleh kosong, Key menandai primary key dengan PRI, dan Default memperlihatkan nilai bawaan. Bentuk singkatnya DESC produk;, hasilnya sama persis.

    Perintah kedua, SHOW CREATE TABLE, menampilkan perintah lengkap yang MySQL simpan untuk tabel itu:

    SHOW CREATE TABLE produk\G
    *************************** 1. row ***************************
           Table: produk
    Create Table: CREATE TABLE `produk` (
      `id` int NOT NULL AUTO_INCREMENT,
      `nama` varchar(100) NOT NULL,
      `harga` int NOT NULL,
      `stok` int NOT NULL DEFAULT '0',
      `dibuat_pada` datetime DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    1 row in set (0.00 sec)

    Tanda \G di akhir membuat output ditampilkan memanjang ke bawah, lebih enak dibaca daripada tabel lebar. Di sini terlihat detail yang tidak muncul di DESCRIBE: engine penyimpanan InnoDB dan charset utf8mb4. Kebiasaan yang berguna: saat kamu menangani database yang dibuat orang lain, SHOW CREATE TABLE adalah cara tercepat memahami struktur aslinya. Kami sendiri selalu memulai dari perintah ini setiap kali mengambil alih database peninggalan developer sebelumnya di proyek klien.

    ALTER TABLE: Mengubah Struktur Tanpa Membuat Ulang

    Struktur tabel hampir tidak pernah benar sejak hari pertama. Kebutuhan berubah, kolom baru dibutuhkan. Untuk itu ada ALTER TABLE. Misalnya kita sadar produk perlu deskripsi:

    ALTER TABLE produk ADD COLUMN deskripsi TEXT;
    Query OK, 0 rows affected (0.05 sec)
    Records: 0  Duplicates: 0  Warnings: 0

    Kolom deskripsi bertipe TEXT kini menempel di posisi paling akhir. Mau menaruh kolom di posisi tertentu? Pakai AFTER:

    ALTER TABLE produk ADD COLUMN kategori VARCHAR(50) AFTER nama;

    Mengubah kolom yang sudah ada juga bisa. Misalnya 100 karakter untuk nama produk ternyata kurang:

    ALTER TABLE produk MODIFY COLUMN nama VARCHAR(150) NOT NULL;

    Hati-hati dengan MODIFY: kamu harus menulis ulang definisi kolom secara utuh. Kalau kolom aslinya NOT NULL lalu kamu tulis MODIFY COLUMN nama VARCHAR(150) saja, atribut NOT NULL ikut hilang. Sedangkan untuk mengganti nama kolom sekaligus definisinya, pakai CHANGE:

    ALTER TABLE produk CHANGE COLUMN deskripsi keterangan TEXT;

    Menghapus kolom pakai DROP COLUMN:

    ALTER TABLE produk DROP COLUMN keterangan;

    Jalankan DESCRIBE produk; lagi setelah setiap perubahan. Membiasakan diri memverifikasi hasil adalah kebiasaan kecil yang menyelamatkan kamu dari banyak kejutan.

    DROP TABLE dan DROP DATABASE: Perintah yang Tidak Bisa Dibatalkan

    Perintah DROP TABLE produk; menghapus tabel beserta seluruh isinya, seketika, tanpa konfirmasi, dan tanpa tombol undo. DROP DATABASE toko_online; lebih ganas lagi: seluruh database dan semua tabel di dalamnya lenyap. Dua kebiasaan aman yang layak kamu pasang sejak sekarang:

    1. Sebelum menjalankan DROP, jalankan SELECT DATABASE(); untuk memastikan kamu ada di database yang benar. Menghapus tabel di database yang salah adalah cerita horor klasik di dunia kerja.
    2. Gunakan bentuk DROP TABLE IF EXISTS nama_tabel; di skrip. Kalau tabelnya tidak ada, MySQL hanya memberi warning, bukan error yang menghentikan skrip.

    Untuk latihan, silakan coba hapus lalu buat ulang tabel produk dengan perintah CREATE TABLE di atas. Mengulang siklus buat, ubah, hapus, buat lagi adalah cara tercepat membuat sintaksnya melekat di kepala.

    Troubleshooting: Error yang Paling Sering Dialami Pemula

    ERROR 1046 (3D000): No database selected

    Kamu menjalankan CREATE TABLE atau SELECT tapi lupa memilih database. MySQL tidak tahu tabelnya mau ditaruh di mana. Solusinya jalankan USE toko_online; dulu, lalu ulangi perintahmu. Alternatifnya, sebut nama database langsung di perintah: CREATE TABLE toko_online.produk (...). Error ini juga sering muncul setelah kamu keluar masuk sesi mysql, karena pilihan database tidak tersimpan antar sesi.

    ERROR 1050 (42S01): Table ‘produk’ already exists

    Kamu menjalankan CREATE TABLE produk padahal tabel itu sudah ada, biasanya karena mengulang skrip yang sama dua kali. Ada dua jalan keluar. Kalau tabel lama memang mau dibuang, hapus dulu dengan DROP TABLE produk; lalu buat ulang. Kalau kamu hanya ingin skrip tidak error saat diulang, pakai CREATE TABLE IF NOT EXISTS produk (...);. Tapi ingat, IF NOT EXISTS tidak memperbarui struktur; kalau definisi barumu beda dengan tabel lama, yang berlaku tetap tabel lama.

    ERROR 1064 (42000): You have an error in your SQL syntax

    Sembilan dari sepuluh kasus di CREATE TABLE, penyebabnya koma. Contoh yang salah:

    CREATE TABLE produk (
        id INT AUTO_INCREMENT PRIMARY KEY,
        nama VARCHAR(100) NOT NULL,
    );

    Koma setelah NOT NULL di kolom terakhir membuat MySQL mengira masih ada definisi kolom lagi, lalu kaget ketemu tanda tutup kurung. Pesan errornya selalu menyebut potongan query di dekat lokasi masalah, misalnya near ')' at line 4. Baca bagian near '...' itu, lalu periksa karakter tepat sebelum posisi tersebut. Kebalikannya juga sering: lupa koma di antara dua kolom, yang membuat MySQL membaca dua definisi sebagai satu baris kacau.

    ERROR 1049 (42000): Unknown database ‘toko_online’

    Muncul saat USE menunjuk database yang tidak ada. Penyebab paling umum: salah ketik nama, atau kamu sedang di server yang berbeda dari tempat database dibuat. Ingat juga soal huruf besar kecil di Linux: USE Toko_Online; gagal kalau nama aslinya toko_online. Jalankan SHOW DATABASES; untuk melihat nama persisnya.

    Rangkuman dan Lanjut ke Bagian Berikutnya

    Hari ini kamu sudah memegang siklus hidup lengkap sebuah struktur database: CREATE DATABASE dan USE untuk menyiapkan wadah, CREATE TABLE dengan AUTO_INCREMENT PRIMARY KEY, NOT NULL, dan DEFAULT untuk membangun tabel yang disiplin, DESCRIBE dan SHOW CREATE TABLE untuk membaca struktur, serta ALTER TABLE dan DROP TABLE untuk mengubah dan membongkar. Ditambah empat error klasik yang sekarang tidak akan membuat kamu panik lagi.

    Satu hal sengaja belum kita dalami: kenapa nama pakai VARCHAR(100) dan bukan TEXT, dan kenapa INT untuk harga sebenarnya bukan pilihan terbaik untuk semua kasus uang. Pemilihan tipe data yang tepat berdampak langsung ke ukuran penyimpanan dan kecepatan query. Itu jatah bagian berikutnya, “Belajar Database dari Nol #4: Tipe Data MySQL yang Tepat”, yang terbit menyusul dan bisa kamu pantau di halaman hub seri ini. Sampai jumpa di sana.

    Referensi

  • Belajar Database dari Nol #2: Konsep Database Relasional

    Belajar Database dari Nol #2: Konsep Database Relasional

    Database relasional adalah database yang menyimpan data dalam bentuk tabel-tabel yang saling berhubungan. Setiap tabel punya baris dan kolom, mirip lembar Excel, tapi dengan aturan yang jauh lebih ketat: tipe data dikunci, setiap baris wajib punya identitas unik, dan hubungan antar tabel dijaga oleh sistem, bukan oleh ingatan manusia. Model inilah yang dipakai MySQL, PostgreSQL, SQL Server, dan hampir semua aplikasi yang menyimpan data transaksi.

    Artikel ini bagian kedua dari seri Belajar Database dari Nol. Di bagian ini kita belum banyak mengetik perintah. Fokusnya memahami cara berpikir database relasional dulu, supaya waktu mulai praktik membuat tabel di bagian berikutnya kamu tahu alasan di balik setiap keputusan, bukan sekadar meniru kode.

    Prasyaratnya cuma satu: MySQL 8.4 sudah terpasang dan kamu bisa masuk ke prompt mysql>. Kalau belum, kerjakan dulu Belajar Database dari Nol #1: Kenalan & Install MySQL 8.4, lalu kembali ke sini.

    Tabel, Baris, Kolom, dan Skema: Dipetakan dari Data Toko Nyata

    Bayangkan kamu punya toko alat tulis dan mencatat pelanggan di buku. Setiap pelanggan dicatat namanya, nomor HP, dan kotanya. Dalam database relasional, catatan itu jadi sebuah tabel seperti ini:

    id nama no_hp kota
    1 Budi Santoso 081234567890 Semarang
    2 Siti Aminah 085612345678 Banjarnegara
    3 Andi Wijaya 089876543210 Purwokerto

    Dari tabel itu kita bisa memetakan empat istilah dasar:

    • Tabel: satu kumpulan data sejenis. Di sini tabel pelanggan. Nanti akan ada tabel lain seperti produk dan pesanan.
    • Baris (record atau row): satu entitas utuh. Baris pertama adalah data lengkap milik Budi Santoso.
    • Kolom (field atau column): satu jenis informasi yang sama untuk semua baris. Kolom kota berisi kota untuk setiap pelanggan.
    • Skema: rancangan strukturnya. Skema menjawab pertanyaan tabel apa saja yang ada, kolomnya apa, tipe datanya apa, dan bagaimana tabel-tabel itu berhubungan. Di MySQL, istilah schema dan database sering dipakai bergantian untuk hal yang sama.

    Perbedaan penting dengan buku catatan biasa: di database relasional, struktur ditetapkan lebih dulu. Kamu mendeklarasikan bahwa no_hp itu teks maksimal 15 karakter, baru kemudian mengisi datanya. Data yang tidak cocok dengan struktur akan ditolak. Kaku, tapi justru kekakuan ini yang membuat data tetap rapi setelah bertahun-tahun dipakai.

    Primary Key dan UNIQUE: Kenapa Setiap Baris Butuh Identitas

    Perhatikan kolom id di tabel tadi. Kenapa perlu, padahal sudah ada nama? Karena nama bisa kembar. Kalau ada dua Budi Santoso dan kamu ingin menghapus salah satunya, database harus tahu persis Budi yang mana. Di sinilah primary key berperan: satu kolom (atau kombinasi kolom) yang nilainya dijamin unik dan tidak boleh kosong untuk setiap baris.

    Aturan primary key sederhana:

    • Nilainya wajib unik. Tidak boleh ada dua baris dengan id sama.
    • Tidak boleh NULL alias kosong.
    • Satu tabel hanya boleh punya satu primary key.
    • Idealnya nilainya tidak pernah berubah. Karena itu praktik umum memakai angka yang naik otomatis (AUTO_INCREMENT), bukan data asli seperti nomor HP yang bisa ganti.

    Lalu bagaimana dengan kolom yang memang tidak boleh kembar tapi bukan identitas utama, misalnya no_hp atau email? Untuk itu ada constraint UNIQUE. Bedanya dengan primary key: UNIQUE boleh lebih dari satu dalam satu tabel dan nilainya boleh NULL.

    Sebagai gambaran, beginilah bentuk deklarasinya dalam SQL. Detail praktiknya kita bahas di bagian 3, jadi sekarang cukup dibaca dulu:

    CREATE TABLE pelanggan (
        id INT AUTO_INCREMENT PRIMARY KEY,
        nama VARCHAR(100) NOT NULL,
        no_hp VARCHAR(15) UNIQUE,
        kota VARCHAR(50)
    );

    Kalau kamu mencoba memasukkan dua pelanggan dengan no_hp sama, MySQL akan menolak dengan pesan seperti ini:

    ERROR 1062 (23000): Duplicate entry '081234567890' for key 'pelanggan.no_hp'

    Penolakan ini bukan gangguan. Ini database sedang melindungi kamu dari data ganda yang di spreadsheet biasanya baru ketahuan setelah jadi masalah.

    Relasi Antar Tabel: Gambaran Besar Pelanggan dan Pesanan

    Sekarang bagian yang membuat kata “relasional” bermakna. Toko tadi mulai mencatat pesanan. Cara naif: tambahkan kolom pesanan di tabel pelanggan. Masalahnya, satu pelanggan bisa memesan berkali-kali. Mau bikin kolom pesanan_1, pesanan_2, pesanan_3? Sampai berapa? Struktur seperti ini cepat berantakan.

    Solusi relasional: pisahkan jadi dua tabel, lalu hubungkan lewat primary key. Tabel pesanan cukup menyimpan id_pelanggan, yaitu rujukan ke baris di tabel pelanggan:

    id id_pelanggan tanggal total
    1 2 2026-07-20 45000
    2 1 2026-07-21 120000
    3 2 2026-07-25 78000

    Baca baris pertama: pesanan nomor 1 dibuat oleh pelanggan dengan id 2, yaitu Siti Aminah. Nama, nomor HP, dan kota Siti tidak perlu ditulis ulang. Cukup disimpan sekali di tabel pelanggan, dan semua pesanannya menunjuk ke sana. Kalau Siti ganti nomor HP, kamu update satu baris, dan semua data pesanan otomatis tetap konsisten.

    Kolom id_pelanggan ini nantinya akan kita resmikan sebagai foreign key, yaitu aturan yang membuat MySQL menolak pesanan dari id pelanggan yang tidak ada. Cara menggabungkan dua tabel ini dalam satu hasil query namanya JOIN. Keduanya dapat porsi bahasan sendiri di bagian 11 sampai 13 seri ini. Untuk sekarang, yang penting kamu pegang gambaran besarnya: data dipecah ke tabel-tabel kecil yang fokus, lalu dihubungkan lewat kunci.

    Database Relasional vs Spreadsheet: Kapan Excel Tidak Cukup

    Pertanyaan yang wajar: kalau bentuknya sama-sama tabel, kenapa tidak pakai Excel atau Google Sheets saja? Untuk catatan pribadi atau data kecil yang dipegang satu orang, spreadsheet memang cukup. Tapi ada tiga batas yang membuat aplikasi serius selalu pindah ke RDBMS (Relational Database Management System) seperti MySQL.

    Integritas data

    Spreadsheet menerima apa saja. Kolom tanggal bisa terisi “besok”, nomor HP bisa terisi nama, dan baris pelanggan bisa terhapus padahal masih punya pesanan. RDBMS menolak semua itu lewat tipe data, constraint NOT NULL, UNIQUE, dan foreign key. Kesalahan dicegah saat data masuk, bukan ditemukan saat laporan sudah kacau.

    Banyak pengguna sekaligus

    Dua kasir yang mengedit satu file Excel bersamaan itu resep bencana. MySQL dirancang untuk ratusan koneksi bersamaan, lengkap dengan mekanisme penguncian dan transaksi supaya dua perubahan yang bertabrakan tidak saling menimpa. Konsep transaksinya kita bahas di bagian 19.

    Skala dan kecepatan pencarian

    Spreadsheet mulai berat di puluhan ribu baris. Tabel MySQL yang diberi index tetap bisa menjawab pencarian dalam hitungan milidetik meski berisi jutaan baris. Di proyek klien Arrazy, tabel transaksi sistem aplikasi yang kami bangun rutin tumbuh terus setiap hari, dan itu skenario normal yang memang jadi habitat database relasional, bukan kondisi darurat.

    Ringkasnya dalam satu tabel perbandingan:

    Aspek Spreadsheet RDBMS (MySQL)
    Validasi data Opsional, mudah dilanggar Dipaksa lewat tipe data dan constraint
    Pengguna bersamaan Rawan konflik Dirancang untuk banyak koneksi
    Jutaan baris Lambat atau crash Normal, dibantu index
    Hubungan antar data Manual lewat VLOOKUP Bawaan lewat foreign key dan JOIN
    Diakses aplikasi lain Sulit dan rapuh Standar lewat SQL

    Istilah yang Akan Sering Muncul: SQL, Query, DDL, dan DML

    Sebelum lanjut ke praktik, kenali dulu empat istilah yang akan muncul terus sepanjang seri ini.

    • SQL (Structured Query Language): bahasa standar untuk berbicara dengan database relasional. MySQL, PostgreSQL, dan SQL Server semuanya memakai SQL dengan sedikit perbedaan dialek.
    • Query: satu perintah SQL yang kamu kirim ke database. Bisa berupa permintaan data, bisa juga perintah mengubah sesuatu.
    • DDL (Data Definition Language): kelompok perintah untuk mengatur struktur. Contohnya CREATE TABLE, ALTER TABLE, DROP TABLE. DDL mengubah wadahnya.
    • DML (Data Manipulation Language): kelompok perintah untuk mengelola isi. Contohnya INSERT, SELECT, UPDATE, DELETE. DML mengubah atau membaca isinya.

    Analogi sederhananya: DDL itu membangun dan merenovasi rak arsip, DML itu memasukkan, membaca, mengganti, dan membuang berkas di rak tersebut. Bagian 3 sampai 5 seri ini urutannya persis mengikuti logika itu: buat wadahnya dulu (DDL), baru isi datanya (DML).

    Untuk memastikan lingkunganmu siap, masuk ke MySQL dan jalankan satu query pertama:

    mysql -u root -p

    Lalu di prompt mysql>:

    SHOW DATABASES;

    Output yang diharapkan pada instalasi MySQL 8.4 yang masih bersih:

    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    4 rows in set (0.00 sec)

    Empat database itu bawaan sistem, tempat MySQL menyimpan konfigurasi dan metadata dirinya sendiri. Jangan diutak-atik. Database milikmu akan kita buat sendiri di bagian berikutnya.

    Troubleshooting: Error yang Sering Dialami Pemula

    ERROR 2002 (HY000): Can’t connect to local MySQL server through socket

    Penyebab: service MySQL belum jalan. Klien mencoba menyambung, tapi tidak ada server yang mendengarkan. Solusi di Ubuntu atau Debian:

    sudo systemctl start mysql
    sudo systemctl status mysql

    Pastikan statusnya active (running). Kalau ingin MySQL otomatis jalan setiap komputer menyala, jalankan sudo systemctl enable mysql.

    ERROR 1045 (28000): Access denied for user ‘root’@’localhost’

    Penyebab: password salah, atau kamu masuk tanpa opsi -p padahal akun root sudah diberi password saat instalasi di bagian 1. Solusi: ulangi dengan mysql -u root -p dan ketik password dengan teliti. Ingat, saat mengetik password di terminal memang tidak muncul karakter apa pun. Itu normal, bukan keyboard rusak.

    ERROR 1064 (42000): You have an error in your SQL syntax

    Penyebab: salah ketik perintah. Yang paling sering dialami pemula: lupa titik koma di akhir perintah, salah eja kata kunci seperti DATABSE, atau memakai tanda kutip keriting hasil copy dari dokumen Word. Solusi: baca pesan errornya, MySQL menunjukkan potongan teks di dekat lokasi salahnya. Ketik ulang perintah secara manual, jangan copy paste dari sumber yang formatnya tidak jelas.

    Prompt berubah jadi tanda panah -> dan perintah tidak jalan

    Penyebab: kamu menekan Enter sebelum menutup perintah, biasanya karena lupa titik koma. MySQL mengira perintahmu belum selesai dan menunggu lanjutannya. Solusi: kalau memang tinggal kurang titik koma, ketik ; lalu Enter. Kalau mau membatalkan perintah yang setengah jadi, ketik \c lalu Enter, dan prompt kembali normal ke mysql>.

    Rangkuman dan Lanjut ke Bagian 3

    Sampai sini kamu sudah memegang fondasi konsepnya. Database relasional menyimpan data dalam tabel berisi baris dan kolom, strukturnya ditetapkan lewat skema, setiap baris diberi identitas lewat primary key, dan tabel-tabel saling terhubung lewat kunci. Dibanding spreadsheet, RDBMS menang di integritas data, dukungan banyak pengguna, dan skala. Kamu juga sudah kenal peta istilah SQL, query, DDL, dan DML yang jadi bahasa sehari-hari sepanjang seri ini.

    Di bagian berikutnya, konsep ini mulai kita eksekusi: Belajar Database dari Nol #3: Membuat Database & Tabel MySQL. Kita akan membuat database toko dari nol, lengkap dengan tabel pelanggan dan aturan-aturannya. Artikelnya terbit menyusul, pantau daftar lengkapnya di halaman hub Belajar Database.

    Referensi

  • Belajar Database dari Nol #1: Kenalan & Install MySQL 8.4

    Belajar Database dari Nol #1: Kenalan & Install MySQL 8.4

    Cara install MySQL 8.4 sebenarnya sederhana: di Windows pakai installer MSI resmi, di Ubuntu pakai repositori APT dari MySQL, di macOS pakai Homebrew. Setelah terpasang, kamu login lewat terminal dengan mysql -u root -p lalu cek versinya dengan SELECT VERSION();. Artikel ini memandu ketiganya langkah demi langkah, plus solusi untuk error yang paling sering muncul di instalasi pertama.

    Ini bagian pertama dari seri Belajar Database dari Nol, seri tutorial 26 bagian yang membawa kamu dari nol sampai bisa mendesain dan mengelola database sendiri. Kita pakai MySQL 8.4 karena statusnya LTS (Long Term Support), didukung sampai tahun 2032, dan paling banyak dipakai di industri. Sebelum praktik, kita luruskan dulu satu hal: apa sebenarnya database itu.

    Apa Itu Database dan Kenapa Aplikasi Membutuhkannya

    Database adalah kumpulan data yang disimpan secara terstruktur supaya mudah dicari, diubah, dan dijaga konsistensinya. Aplikasi yang mengelola database itu disebut DBMS, singkatan dari Database Management System. MySQL adalah salah satu DBMS. Jadi kalau ada yang bilang “databasenya MySQL”, maksudnya data mereka dikelola oleh software MySQL.

    Pertanyaan yang wajar dari pemula: kenapa tidak pakai Excel atau file teks saja? Untuk data pribadi yang diakses satu orang, Excel memang cukup. Masalah muncul saat data dipakai aplikasi:

    • Akses bersamaan. Ratusan pengguna bisa menulis data di detik yang sama. File Excel akan korup atau saling menimpa. DBMS mengatur antrean ini secara otomatis.
    • Kecepatan pencarian. Mencari satu baris di antara jutaan baris di file teks berarti membaca seluruh file. DBMS punya index sehingga pencarian tetap cepat walau datanya jutaan baris.
    • Konsistensi. Saat transfer saldo gagal di tengah jalan, DBMS bisa membatalkan seluruh operasi supaya uang tidak hilang sebagian. File biasa tidak punya mekanisme ini.
    • Aturan data. DBMS bisa memaksa kolom email selalu terisi atau stok tidak boleh minus. Di Excel, aturan seperti ini gampang dilanggar.

    Di Arrazy, hampir semua sistem aplikasi yang kami bangun untuk klien memakai database relasional seperti MySQL atau PostgreSQL di belakangnya, dari sistem inventaris gudang sampai backend API. Skill database adalah fondasi yang akan terus kamu pakai selama berkarier di dunia software.

    Persiapan Sebelum Install MySQL 8.4

    Yang kamu butuhkan hanya komputer dengan salah satu sistem operasi berikut, koneksi internet, dan ruang disk sekitar 2 GB:

    Sistem Operasi Metode Install Sumber
    Windows 10/11 Installer MSI dev.mysql.com/downloads
    Ubuntu 22.04/24.04 Repositori APT MySQL dev.mysql.com/downloads/repo/apt
    macOS (Apple Silicon/Intel) Homebrew brew.sh

    Satu catatan penting: selama proses install kamu akan diminta membuat password untuk user root. Catat password ini baik-baik. Sebagian besar masalah login di kemudian hari berawal dari password root yang lupa.

    Cara Install MySQL 8.4 di Windows

    Mulai versi 8.1, MySQL di Windows tidak lagi memakai aplikasi “MySQL Installer” yang lama. Sekarang instalasinya lewat paket MSI langsung, lalu konfigurasi lewat MySQL Configurator.

    1. Buka https://dev.mysql.com/downloads/mysql/, pilih versi 8.4.x LTS, lalu unduh Windows (x86, 64-bit), MSI Installer yang ukurannya besar (bukan yang web installer). Kamu boleh klik “No thanks, just start my download” tanpa login Oracle.
    2. Jalankan file mysql-8.4.x-winx64.msi, pilih tipe instalasi Typical, lalu selesaikan wizard-nya.
    3. Di akhir instalasi, centang opsi untuk menjalankan MySQL Configurator. Di sinilah kamu mengatur MySQL sebagai Windows Service, membiarkan port default 3306, dan membuat password root.
    4. Selesaikan Configurator sampai semua langkah bertanda hijau.

    Supaya perintah mysql bisa dipanggil dari terminal mana saja, tambahkan folder C:\Program Files\MySQL\MySQL Server 8.4\bin ke environment variable Path. Caranya: cari “Edit the system environment variables” di Start Menu, klik Environment Variables, pilih Path, klik Edit, lalu New dan tempel path tadi. Buka Command Prompt baru dan cek:

    mysql --version

    Output yang diharapkan kurang lebih seperti ini (versi minor bisa berbeda):

    mysql  Ver 8.4.5 for Win64 on x86_64 (MySQL Community Server - GPL)

    Cara Install MySQL 8.4 di Ubuntu dan Linux Lain

    Repositori bawaan Ubuntu 22.04 dan 24.04 masih menyediakan MySQL 8.0. Untuk mendapatkan 8.4 LTS, tambahkan dulu repositori APT resmi dari MySQL. Cek versi paket mysql-apt-config terbaru di https://dev.mysql.com/downloads/repo/apt/, lalu jalankan:

    wget https://dev.mysql.com/get/mysql-apt-config_0.8.34-1_all.deb
    sudo dpkg -i mysql-apt-config_0.8.34-1_all.deb

    Akan muncul layar konfigurasi berwarna ungu. Pilih MySQL Server & Cluster, ganti ke mysql-8.4-lts, lalu pilih Ok. Setelah itu install servernya:

    sudo apt update
    sudo apt install mysql-server

    Saat instalasi kamu akan diminta membuat password root. Isi dan catat. Kalau kolomnya kamu kosongkan, MySQL akan memakai mode autentikasi auth_socket, artinya login root hanya bisa lewat sudo mysql. Setelah selesai, pastikan service-nya jalan:

    sudo systemctl status mysql

    Cari baris ini di output:

    Active: active (running)

    Kalau statusnya inactive, nyalakan dengan sudo systemctl start mysql dan aktifkan autostart dengan sudo systemctl enable mysql. Langkah opsional tapi disarankan, jalankan sudo mysql_secure_installation untuk menghapus user anonim dan database test bawaan.

    Cara Install MySQL 8.4 di macOS

    Cara paling praktis di macOS adalah lewat Homebrew. Kalau belum punya Homebrew, install dulu dari https://brew.sh. Lalu jalankan:

    brew install mysql@8.4
    brew services start mysql@8.4

    Formula mysql@8.4 bersifat keg-only, artinya tidak otomatis masuk ke PATH. Tambahkan manual (contoh untuk Mac Apple Silicon dengan zsh):

    echo 'export PATH="/opt/homebrew/opt/mysql@8.4/bin:$PATH"' >> ~/.zshrc
    source ~/.zshrc

    Untuk Mac Intel, ganti /opt/homebrew dengan /usr/local. Instalasi lewat Homebrew membuat user root tanpa password. Rapikan dengan menjalankan mysql_secure_installation dan set password root di sana. Alternatif lain kalau tidak mau pakai Homebrew: unduh installer DMG dari https://dev.mysql.com/downloads/mysql/ dan ikuti wizard-nya, mirip proses di Windows.

    Login Pertama Lewat Terminal dan Verifikasi Instalasi

    Apa pun sistem operasinya, momen kebenarannya sama. Buka terminal (Command Prompt atau PowerShell di Windows) dan jalankan:

    mysql -u root -p

    Arti perintahnya: masuk ke MySQL sebagai user root (-u root) dan minta prompt password (-p). Ketik password yang kamu buat saat instalasi. Password tidak akan terlihat saat diketik, itu normal. Kalau berhasil, kamu akan disambut prompt MySQL:

    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 10
    Server version: 8.4.5 MySQL Community Server - GPL
    
    mysql>

    Sekarang jalankan query pertamamu. Jangan lupa titik koma di akhir:

    SELECT VERSION();

    Outputnya:

    +-----------+
    | VERSION() |
    +-----------+
    | 8.4.5     |
    +-----------+
    1 row in set (0.00 sec)

    Kalau angka yang muncul diawali 8.4, instalasimu sukses. Ketik exit untuk keluar dari prompt MySQL. Selamat, kamu resmi punya server database sendiri.

    Error Umum Saat Install MySQL dan Solusinya

    Tiga error di bawah ini adalah yang paling sering kami temui saat mendampingi pemula maupun saat menyiapkan server klien. Cek satu per satu sebelum panik.

    ERROR 1045: Access denied for user ‘root’@’localhost’

    Penyebab paling umum: password salah, atau di Ubuntu kamu mengosongkan password saat install sehingga root memakai auth_socket. Coba dulu login lewat sudo:

    sudo mysql

    Kalau berhasil masuk, set password root dari dalam prompt MySQL supaya selanjutnya bisa login normal:

    ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'PasswordBaruKamu123';
    FLUSH PRIVILEGES;

    Ganti PasswordBaruKamu123 dengan password pilihanmu. Kalau password benar-benar lupa dan sudo mysql juga ditolak, jalan keluarnya adalah menjalankan server dengan opsi --skip-grant-tables lalu reset password, prosedurnya ada di dokumentasi resmi bagian “How to Reset the Root Password”.

    ERROR 2002: Can’t connect to local MySQL server (service tidak jalan)

    Error ini artinya client tidak menemukan server yang hidup. Di Linux dan macOS cek dengan sudo systemctl status mysql atau brew services list, lalu nyalakan dengan sudo systemctl start mysql atau brew services start mysql@8.4. Di Windows, tekan Win + R, ketik services.msc, cari service bernama MySQL84, klik kanan lalu Start. Kalau service menolak jalan, baca log error di /var/log/mysql/error.log (Ubuntu) atau folder data MySQL di Windows, biasanya penyebab pastinya tertulis di baris terakhir.

    Port 3306 sudah dipakai program lain

    MySQL default berjalan di port 3306. Kalau di komputermu pernah terpasang XAMPP, Laragon, MariaDB, atau MySQL versi lain, port ini bisa bentrok dan service baru gagal start. Cek siapa yang memakai port itu:

    # Linux / macOS
    sudo lsof -i :3306
    
    # Windows (PowerShell)
    netstat -ano | findstr :3306

    Solusinya pilih salah satu: matikan program lama yang menempati port itu (misalnya stop MySQL bawaan XAMPP), uninstall MySQL/MariaDB lama yang sudah tidak dipakai, atau ubah port MySQL 8.4 lewat file konfigurasi (my.ini di Windows, /etc/mysql/mysql.conf.d/mysqld.cnf di Ubuntu) dengan mengganti nilai port=3306 lalu restart service. Untuk mengikuti seri ini, cara paling bersih adalah satu MySQL saja di port default.

    Menyiapkan MySQL Client dan GUI Opsional

    Sepanjang seri ini kita akan bekerja terutama lewat mysql client di terminal, program yang barusan kamu pakai untuk login. Alasannya sederhana: semua perintah SQL yang kamu ketik manual akan menempel lebih lama di ingatan, dan di server production nanti sering kali terminal adalah satu-satunya akses yang tersedia.

    Meski begitu, GUI boleh dipakai sebagai pendamping. Beberapa pilihan yang umum:

    • MySQL Workbench, GUI resmi dari MySQL, gratis, tersedia untuk Windows, Linux, dan macOS. Unduh di https://dev.mysql.com/downloads/workbench/. Saat membuat koneksi baru, isi host 127.0.0.1, port 3306, user root, dan password yang tadi kamu buat.
    • DBeaver Community, gratis dan mendukung banyak DBMS sekaligus, berguna nanti saat kita menyentuh PostgreSQL di bagian 24.
    • phpMyAdmin, berbasis web, sering ditemui di shared hosting.

    Saran kami untuk pemula: install MySQL Workbench sebagai alat bantu melihat isi tabel, tapi tetap kerjakan latihan di terminal. Kombinasi ini yang paling cepat membangun pemahaman.

    Rangkuman dan Bagian Berikutnya

    Hari ini kamu sudah paham beda database, DBMS, dan alasan aplikasi tidak cukup pakai Excel. Kamu juga sudah praktik cara install MySQL 8.4 LTS di sistem operasimu, login pertama dengan mysql -u root -p, memverifikasi instalasi lewat SELECT VERSION();, dan tahu cara keluar dari tiga error paling umum.

    Di bagian berikutnya, “Belajar Database dari Nol #2: Konsep Database Relasional”, kita bahas cara MySQL menyusun data dalam tabel, baris, kolom, dan primary key, fondasi berpikir sebelum menulis SQL lebih jauh. Artikelnya terbit menyusul dan bisa kamu pantau di halaman hub seri Belajar Database. Sampai jumpa di bagian dua.

    Referensi