Memilih Tipe Data yang Tepat Sejak Awal

Waktu bikin tabel baru, kita sering buru-buru: mau cepat ada fitur jalan, urusan tipe kolom dianggap detail kecil yang bisa dibenerin nanti. Masalahnya, "nanti" itu biasanya datang saat tabel sudah berisi jutaan baris dan relasi ke banyak tempat, sehingga perubahan tipe kolom bukan lagi soal satu baris migration, tapi proyek yang harus direncanakan matang-matang. Dari pengalaman menangani sistem yang berurusan dengan data pasien dan transaksi dalam jumlah besar, saya melihat pola ini berulang: keputusan tipe data di hari pertama menentukan seberapa mulus sistem itu berkembang bertahun-tahun ke depan.

Kenapa "string untuk angka" jadi jebakan klasik

Contoh yang umum ditemui: kolom nomor rekam medis, nomor telepon, atau kode tertentu disimpan sebagai VARCHAR padahal isinya angka. Awalnya terasa aman, karena fleksibel dan tidak perlu mikir batas nilai. Tapi begitu data membesar, masalah baru muncul satu per satu: sorting jadi salah (string "10" dianggap lebih kecil dari "9"), index jadi lebih besar dan lebih lambat dibanding tipe numerik, dan validasi di level aplikasi jadi beban tambahan karena database tidak lagi menjamin isinya angka.

Sebaliknya, ada juga kasus kebalikan yang sama merepotkan: memaksakan tipe numerik untuk sesuatu yang sebenarnya identifier, misalnya nomor rekening atau kode wilayah yang bisa diawali angka nol. Begitu disimpan sebagai INT, angka nol di depan hilang, dan baru ketahuan saat laporan keluar dengan data yang salah. Prinsipnya sederhana: tipe data itu bukan cuma soal "isinya angka atau bukan", tapi soal apa operasi yang akan dilakukan terhadap data itu—dihitung, atau cuma diidentifikasi.

Dampak yang baru terasa saat skala membesar

Salah pilih tipe kolom biasanya baru terasa saat tabel sudah tumbuh besar:

  • Index pada kolom string umumnya lebih besar dan lebih lambat dibanding index pada kolom numerik atau tipe yang lebih ringkas, sehingga query yang tadinya instan mulai terasa lambat.
  • Operasi agregasi (SUM, AVG, perbandingan rentang) pada kolom yang seharusnya numerik tapi disimpan sebagai string jadi tidak bisa dilakukan langsung di database, harus di-cast dulu, dan itu membebani setiap query.
  • Join antar tabel yang tipe kolomnya tidak konsisten (misalnya satu tabel pakai BIGINT, tabel lain pakai VARCHAR untuk foreign key yang sama) memaksa database melakukan konversi implisit, yang sering bikin index tidak terpakai sama sekali.

Yang lebih berat lagi adalah biaya migrasinya. Mengubah tipe kolom di tabel kecil hanya butuh satu perintah ALTER TABLE. Di tabel besar, perintah yang sama bisa mengunci tabel untuk waktu lama, sehingga di banyak sistem produksi itu bukan pilihan—harus dikerjakan bertahap: bikin kolom baru, migrasi data sedikit demi sedikit sambil aplikasi tetap jalan, baru kemudian ganti nama kolom. Jauh lebih mahal dibanding kalau tipe kolomnya sudah benar sejak awal.

Pendekatan praktis saat merancang skema

Beberapa kebiasaan yang saya pegang saat mendesain skema baru: pertama, tanya dulu apakah kolom ini akan dihitung secara matematis atau cuma jadi identitas. Kalau cuma identitas—meski isinya angka—perlakukan sebagai string, dan biarkan validasi format (misalnya panjang digit tertentu) ditangani lewat constraint atau validasi aplikasi, bukan lewat tipe kolom. Kedua, untuk angka yang benar-benar akan dihitung, pilih tipe numerik yang sesuai rentang nilainya—jangan asal INT untuk semua, karena kolom yang berpotensi tumbuh besar (misalnya penomoran transaksi di sistem yang terus jalan bertahun-tahun) sebaiknya langsung pakai tipe dengan rentang yang lebih luas, sebagai ilustrasi seperti BIGINT, daripada nanti harus migrasi ulang gara-gara nilainya melewati batas.

Ketiga, untuk kolom yang berhubungan dengan uang, jangan pakai tipe pecahan biner seperti FLOAT atau DOUBLE—pembulatan yang tidak presisi bisa menimbulkan selisih kecil yang terakumulasi jadi masalah besar. Gunakan tipe desimal presisi tetap yang memang dirancang untuk itu. Di Laravel misalnya, migration bisa eksplisit menetapkan presisi dan skala:

$table->decimal('total_biaya', 15, 2);

Keempat, jangan takut sedikit "berlebihan" di awal untuk hal-hal yang jelas akan tumbuh, tapi juga jangan over-engineer semua kolom seolah semuanya akan jadi masalah skala. Fokus pada kolom yang jadi kunci pertumbuhan data: primary key, foreign key, dan kolom yang sering dipakai untuk filter atau agregasi.

Ringkasnya

Tipe data bukan detail teknis yang bisa dianggap remeh di fase awal. Keputusan itu menentukan seberapa mudah sistem diskalakan nanti tanpa harus berhenti total untuk migrasi. Investasi waktu sedikit lebih banyak di awal untuk memikirkan tipe kolom yang tepat, jauh lebih murah dibanding harus membongkar skema saat data sudah besar dan sistem sudah dipakai banyak orang.