Teks adalah tipe data yang paling sering kita tulis di CREATE TABLE: nama pelanggan, kode produk, alamat, sampai deskripsi barang sepanjang paragraf. MySQL punya tiga keluarga string untuk tiga kebutuhan tersebut — CHAR/VARCHAR untuk teks pendek, TEXT untuk teks panjang, dan ENUM untuk daftar nilai tetap. Pada Chapter ini kita akan membedah perbedaan nyata antar keluarga tersebut, membuktikan lewat LENGTH() bagaimana CHAR mengisi ruang tetap sementara VARCHAR menghemat sesuai isi, dan menguji ketegasan ENUM dalam menolak nilai yang tidak terdaftar.
CHAR vs VARCHAR #
CHAR(n) menyimpan teks dengan panjang tetap n karakter: bila isinya lebih pendek, MySQL menambahkan spasi hingga penuh (padding). VARCHAR(n) menyimpan teks dengan panjang variabel maksimal n karakter: isinya disimpan apa adanya plus satu-dua byte informasi panjang. Karena itu CHAR cocok untuk data yang panjangnya konsisten seperti kode ID-001, sedangkan VARCHAR untuk data manusiawi yang panjangnya berubah-ubah. Kita buat percobaannya:
DROP DATABASE IF EXISTS belajar_tipe;
CREATE DATABASE belajar_tipe;
USE belajar_tipe;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE TABLE kode_produk (
kode CHAR(5),
nama VARCHAR(10)
);
INSERT INTO kode_produk VALUES ('AB', 'Buku'), ('CDE', 'Pena ');
SELECT kode, LENGTH(kode) AS byte_kode, nama, LENGTH(nama) AS byte_nama
FROM kode_produk
ORDER BY kode;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
+------+-----------+-------+-----------+
| kode | byte_kode | nama | byte_nama |
+------+-----------+-------+-----------+
| AB | 2 | Buku | 4 |
| CDE | 3 | Pena | 5 |
+------+-----------+-------+-----------+
2 rows in set (0.00 sec)Hasilnya memperlihatkan perilaku khas kedua tipe. Kolom kode menyimpan 'AB' di CHAR(5) yang diisi spasi hingga 5 karakter, tetapi LENGTH() melaporkan 2 — MySQL memangkas spasi belakang hasil padding begitu nilai dibaca kembali, kecuali mode PAD_CHAR_TO_FULL_LENGTH diaktifkan. Sebaliknya, kolom nama menyimpan 'Pena ' di VARCHAR(10) dan mempertahankan spasi belakangnya: LENGTH() melaporkan 5 persis seperti yang kita insert. Preservasi spasi belakang pada VARCHAR ini penting kita sadari karena spasi tersebut dianggap bagian dari data.
Untuk melihat padding yang sesungguhnya tersimpan di CHAR, kita aktifkan sementara mode PAD_CHAR_TO_FULL_LENGTH pada sesi ini:
SET sql_mode = CONCAT(@@sql_mode, ',PAD_CHAR_TO_FULL_LENGTH');
SELECT kode, LENGTH(kode) AS byte_kode FROM kode_produk ORDER BY kode;
SET sql_mode = DEFAULT;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
+-------+-----------+
| kode | byte_kode |
+-------+-----------+
| AB | 5 |
| CDE | 5 |
+-------+-----------+
2 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)Dengan mode tersebut aktif, LENGTH(kode) kini melaporkan 5 untuk kedua baris — itulah isi fisik yang benar-benar tersimpan: CHAR(5) memang menyimpan 'AB ' dan 'CDE ' dengan spasi penambat hingga penuh. Peringatan 1 warning pada baris SET muncul karena pengubahan mode PAD_CHAR_TO_FULL_LENGTH sendiri sudah ditandai usang — kita memakainya di sini hanya untuk membedah mekanisme internal, bukan untuk dipakai permanen. Baris SET sql_mode = DEFAULT mengembalikan sesi ke kondisi normal; mode ini berlaku per sesi sehingga menutup klien juga mengembalikan perilaku bawaan.
Spasi Belakang dan Pembanding #
Karena VARCHAR mempertahankan spasi belakang, nilai 'Pena' dan 'Pena ' adalah dua data yang berbeda di mata collation bawaan MySQL 8.4 (utf8mb4_0900_ai_ci bertipe NO PAD). Kita bisa membuktikannya:
SELECT 'Buku' = 'Buku ' AS perbandingan;
SELECT nama FROM kode_produk WHERE nama = 'Pena';Output yang Dihasilkan:
+--------------+
| perbandingan |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
Empty set (0.00 sec)Pembanding 'Buku' = 'Buku ' menghasilkan 0 — spasi satu karakter membuat keduanya dianggap tidak sama. Akibatnya, query WHERE nama = 'Pena' tidak menemukan baris 'Pena ' yang kita simpan dan mengembalikan Empty set. Ini jebakan klasik ketika data masuk dari formulir web yang ikut menyertakan spasi tak sengaja; solusinya adalah membersihkan input di aplikasi atau dengan fungsi TRIM() sebelum dibandingkan.
Keluarga TEXT untuk Teks Panjang #
CHAR dan VARCHAR menuntut kita menetapkan panjang maksimal. Bila teks bisa sangat panjang — deskripsi produk, isi artikel, catatan log — keluarga TEXT menyediakan wadah dengan batas jauh lebih besar tanpa perlu menentukan panjang. Empat ukurannya:
| Tipe | Panjang maksimal | Kira-kira |
|---|---|---|
TINYTEXT | 255 byte | sebaris tweet |
TEXT | 65.535 byte | satu paragraf panjang |
MEDIUMTEXT | 16.777.215 byte | satu bab buku |
LONGTEXT | 4.294.967.295 byte | novel utuh |
TEXT menyimpan isinya di luar baris tabel sehingga tabel tetap ramping meskipun isinya besar. Isi pendek tetap sah-sah saja disimpan di TEXT:
CREATE TABLE catatan (id INT, isi TEXT);
INSERT INTO catatan VALUES (1, 'Stok pensil perlu dipesan ulang bulan depan.');
SELECT id, isi, CHAR_LENGTH(isi) AS jumlah_karakter FROM catatan;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
+------+----------------------------------------------+-----------------+
| id | isi | jumlah_karakter |
+------+----------------------------------------------+-----------------+
| 1 | Stok pensil perlu dipesan ulang bulan depan. | 44 |
+------+----------------------------------------------+-----------------+
1 row in set (0.00 sec)Teks pendek tersimpan tanpa masalah dan CHAR_LENGTH menghitung 44 karakter dari kalimat tersebut. Aturan praktis pemilihan: teks dengan batas wajar di bawah beberapa ratus karakter memakai VARCHAR(n), sedangkan teks yang bisa tumbuh tanpa batas jelas memakai keluarga TEXT. Sebagai bonus, kolom VARCHAR bisa dijadikan PRIMARY KEY dan index penuh, sedangkan TEXT butuh panjang prefix khusus untuk keperluan tersebut.
ENUM: Daftar Nilai Tetap #
ENUM membatasi kolom pada daftar nilai yang kita definisikan saat membuat tabel. Nilai yang di luar daftar ditolak, dan setiap nilai punya nomor urut internal mulai dari 1. Cocok untuk kolom status yang nilainya memang tertutup:
CREATE TABLE pesanan (
id INT,
status ENUM('baru', 'diproses', 'dikirim', 'selesai')
);
INSERT INTO pesanan VALUES (1, 'baru'), (2, 'dikirim');
SELECT id, status, status + 0 AS nomor_internal
FROM pesanan
ORDER BY id;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
+------+---------+----------------+
| id | status | nomor_internal |
+------+---------+----------------+
| 1 | baru | 1 |
| 2 | dikirim | 3 |
+------+---------+----------------+
2 rows in set (0.00 sec)Kolom status menampilkan teks nilainya, sedangkan status + 0 membuka nomor urut internalnya: 'baru' tersimpan sebagai 1 dan 'dikirim' sebagai 3 sesuai posisi di daftar definisi. MySQL menyimpan nomor tersebut, bukan teks — itulah sebabnya ENUM hanya memakan 1 byte untuk hingga 255 nilai. Sekarang kita uji ketegasannya terhadap nilai liar:
-- @error
INSERT INTO pesanan VALUES (3, 'batal');Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
ERROR 1265 (01000): Data truncated for column 'status' at row 1
ByeServer menolak 'batal' dengan galat Data truncated karena nilai tersebut tidak ada dalam daftar ENUM. Validasi seperti ini gratis dari sisi aplikasi — kita tidak perlu menulis pemeriksaan tambahan di kode. Konsekuensinya, menambah nilai baru ke daftar memerlukan ALTER TABLE, jadi ENUM paling pas untuk daftar yang jarang berubah; untuk kategori yang berkembang terus (misalnya tag artikel) tabel referensi terpisah lebih fleksibel.
Terakhir, kita bereskan database latihan:
DROP DATABASE belajar_tipe;Output yang Dihasilkan:
Query OK, 3 rows affected (0.00 sec)Referensi #
- The CHAR and VARCHAR Types — MySQL 8.4 Reference Manual
- String Data Types — MySQL 8.4 Reference Manual
- The ENUM Type — MySQL 8.4 Reference Manual
- Data Type Storage Requirements — MySQL 8.4 Reference Manual
Teks dan angka sudah lengkap, tetapi aplikasi nyata juga menyimpan waktu: kapan pesanan dibuat, kapan barang dikirim. Chapter berikutnya membedah tipe DATE, TIME, DATETIME, TIMESTAMP, dan YEAR beserta jebakan rentang tahun 2038.