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
Artikel Lainnya di Kategori Database
Database 6 Agustus 2026
Belajar Database dari Nol #2: Konsep Database Relasional
Pahami database relasional: tabel, baris, kolom, primary key, relasi pelanggan-pesanan, plus beda RDBMS vs spreadsheet sebelum praktik SQL di MySQL 8.4.
Baca Artikel
Database 31 Juli 2026
Belajar Database dari Nol #1: Kenalan & Install MySQL 8.4
Bagian pertama seri Belajar Database dari Nol: kenalan konsep database lalu praktik install MySQL 8.4 di Windows, Ubuntu, dan macOS sampai bisa login.
Baca Artikel
Database 12 Agustus 2026
Belajar Database dari Nol #3: Membuat Database & Tabel MySQL
Praktik cara membuat database MySQL 8.4: CREATE DATABASE, USE, CREATE TABLE dengan PRIMARY KEY, DESCRIBE, ALTER TABLE, plus solusi error umum pemula.
Baca ArtikelIngin Membaca Artikel Lainnya?
Temukan lebih banyak insight dan tips tentang teknologi dan bisnis digital.
Lihat Semua Artikel