Data di dunia nyata sering datang dengan tipe yang salah: hasil impor CSV menyimpan angka sebagai teks, aplikasi lama mengirim tanggal sebagai string, dan laporan membutuhkan angka yang ditampilkan sebagai teks rapi. SQL memiliki dua fungsi konversi eksplisit — CAST dan CONVERT — yang mengubah tipe nilai di dalam query tanpa mengubah data yang tersimpan. Pada chapter ini kita berlatih keduanya memakai tabel hasil impor yang seluruh kolomnya bertipe VARCHAR, lalu kita bedah konversi implisit yang berjalan senyap di belakang operator aritmetika dan perbandingan — termasuk kejutan '100abc' yang berubah menjadi angka tanpa error.
Menyiapkan Tabel Latihan #
Tabel impor_stok menirukan hasil impor file CSV: teks, angka, dan tanggal semuanya masuk sebagai VARCHAR:
DROP DATABASE IF EXISTS belajar_fungsi;
CREATE DATABASE belajar_fungsi;
USE belajar_fungsi;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE TABLE impor_stok (
kode VARCHAR(10) NOT NULL,
nama VARCHAR(50) NOT NULL,
jumlah VARCHAR(10) NOT NULL,
harga VARCHAR(12) NOT NULL,
tanggal VARCHAR(20) NOT NULL
);
INSERT INTO impor_stok (kode, nama, jumlah, harga, tanggal) VALUES
('BRG-001', 'Buku Tulis 38', '120', '4500.00', '2026-01-15'),
('BRG-002', 'Pensil 2B', '200', '3000.00', '2026-01-20'),
('BRG-003', 'Penggaris 30 cm', '55', '2500.00', '2026-02-01'),
('BRG-004', 'Spidol Whiteboard', '8', '6000.00', '2026-02-14'),
('BRG-005', 'Buku Gambar A4', '13', '9000.00', '2026-03-05'),
('BRG-006', 'Kertas HVS A4', '5', '25000.00', '2026-03-10');Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0Dengan semua nilai bertipe teks, kolom jumlah tidak bisa dijumlahkan dan kolom tanggal tidak bisa dihitung selisihnya — aritmetika pada VARCHAR akan memicu konversi implisit yang hasilnya tidak kita kendalikan. Konversi eksplisit memberi kita kendali itu.
Konversi Eksplisit dengan CAST #
Sintaksnya CAST(nilai AS tipe). Tipe yang paling sering dipakai: SIGNED (integer bertanda), UNSIGNED (integer tanpa tanda), DECIMAL(m,n), CHAR, dan DATE:
SELECT CAST('42' AS SIGNED) AS teks_ke_bulat,
CAST(15.86 AS DECIMAL(4,1)) AS ke_desimal,
CAST(123 AS CHAR) AS angka_ke_teks;Output yang Dihasilkan:
+---------------+------------+---------------+
| teks_ke_bulat | ke_desimal | angka_ke_teks |
+---------------+------------+---------------+
| 42 | 15.9 | 123 |
+---------------+------------+---------------+
1 row in set (0.00 sec)Ketiga konversi berhasil tanpa peringatan karena nilai sumbernya memang cocok dengan tipe tujuan. Konversi DECIMAL(4,1) membulatkan 15.86 menjadi 15.9 — total empat digit dengan satu di belakang koma. Satu kasus yang wajib kita waspadai adalah UNSIGNED, karena tipe itu tidak boleh negatif:
SELECT CAST(-1 AS UNSIGNED) AS negatif_jadi_unsigned;Output yang Dihasilkan:
+-----------------------+
| negatif_jadi_unsigned |
+-----------------------+
| 18446744073709551615 |
+-----------------------+
1 row in set (0.00 sec)Hasilnya bukan error dan bukan 0, melainkan 18446744073709551615 — nilai terbesar BIGINT UNSIGNED. Konversi ke UNSIGNED “membungkus” angka negatif ke ujung atas rentangnya, sebuah perilaku warisan yang bisa merusak perhitungan bila lolos tanpa disadari.
CAST pada Data Tabel #
Penerapan sesungguhnya: mengubah kolom-kolom hasil impor menjadi tipe yang benar di dalam query, sebelum data dipakai untuk perhitungan:
SELECT kode, nama,
CAST(jumlah AS SIGNED) AS jumlah_angka,
CAST(harga AS DECIMAL(10,2)) AS harga_angka,
CAST(tanggal AS DATE) AS tanggal_date
FROM impor_stok
ORDER BY kode;Output yang Dihasilkan:
+---------+-------------------+--------------+-------------+--------------+
| kode | nama | jumlah_angka | harga_angka | tanggal_date |
+---------+-------------------+--------------+-------------+--------------+
| BRG-001 | Buku Tulis 38 | 120 | 4500.00 | 2026-01-15 |
| BRG-002 | Pensil 2B | 200 | 3000.00 | 2026-01-20 |
| BRG-003 | Penggaris 30 cm | 55 | 2500.00 | 2026-02-01 |
| BRG-004 | Spidol Whiteboard | 8 | 6000.00 | 2026-02-14 |
| BRG-005 | Buku Gambar A4 | 13 | 9000.00 | 2026-03-05 |
| BRG-006 | Kertas HVS A4 | 5 | 25000.00 | 2026-03-10 |
+---------+-------------------+--------------+-------------+--------------+
6 rows in set (0.00 sec)Query ini tidak mengubah tabel impor_stok sama sekali — CAST hanya mengubah tipe di dalam hasil query. Bila data hasil impor sudah dipastikan bersih, langkah permanennya adalah ALTER TABLE ... MODIFY untuk mengganti tipe kolom; CAST lebih cocok untuk pemeriksaan dan perhitungan sementara sebelum keputusan struktur diambil.
CONVERT: Konversi Tipe dan Character Set #
CONVERT(nilai, tipe) setara dengan CAST(nilai AS tipe). Bentuk keduanya berbeda sama sekali fungsi: CONVERT(nilai USING charset) mengubah character set sebuah teks:
SELECT CONVERT('42', SIGNED) AS ke_tipe,
CONVERT('café' USING ascii) AS ke_ascii,
CHARSET(CONVERT('café' USING ascii)) AS charset_hasil;Output yang Dihasilkan:
+---------+----------+---------------+
| ke_tipe | ke_ascii | charset_hasil |
+---------+----------+---------------+
| 42 | caf? | ascii |
+---------+----------+---------------+
1 row in set (0.00 sec)Teks 'café' yang dikonversi ke ascii menjadi 'caf?' — huruf é tidak ada di ASCII sehingga diganti tanda tanya. Bentuk USING berguna saat bertukar data dengan sistem lama yang hanya menerima latin1 atau ascii; untuk kebutuhan sebaliknya (menyimpan teks apa pun dengan aman), utf8mb4 adalah character set standar yang kita pakai di seluruh course ini.
Konversi Implisit: Senyap tapi Nyata #
MySQL diam-diam mengonversi string menjadi angka ketika string dipakai di operasi aritmetika. Teks '100' ditambah 5 menghasilkan 105, bukan error:
SELECT '100' + 5 AS hasil_tambah;Output yang Dihasilkan:
+--------------+
| hasil_tambah |
+--------------+
| 105 |
+--------------+
1 row in set (0.00 sec)Masalahnya muncul saat teksnya tidak sepenuhnya angka. MySQL membaca angka dari awal string lalu berhenti di karakter pertama yang bukan angka — dan hanya memberi tahu lewat warning:
SELECT '100abc' + 0 AS nilai_terbaca;
SHOW WARNINGS;Output yang Dihasilkan:
+---------------+
| nilai_terbaca |
+---------------+
| 100 |
+---------------+
1 row in set, 1 warning (0.00 sec)
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '100abc' |
+---------+------+--------------------------------------------+
1 row in set (0.00 sec)Nilai 100 terbaca dari awal string, abc dibuang, dan SHOW WARNINGS membongkar peringatan 1292 Truncated incorrect DOUBLE value: '100abc'. Tanpa SHOW WARNINGS di sesi yang sama, konversi semacam ini lolos tanpa terlihat — itulah mengapa data hasil impor yang “kelihatannya angka” tetap perlu diaudit. Konversi implisit juga bekerja pada perbandingan tanggal: literal string '2026-01-15' otomatis menjadi DATE ketika dibandingkan dengan nilai DATE:
SELECT kode FROM impor_stok
WHERE CAST(tanggal AS DATE) = '2026-01-15';Output yang Dihasilkan:
+---------+
| kode |
+---------+
| BRG-001 |
+---------+
1 row in set (0.00 sec)Baris BRG-001 cocok karena literal '2026-01-15' dikonversi implisit ke DATE oleh pembandingnya. Pola WHERE kolom_tanggal = 'literal string' ini sah dan lazim dipakai di aplikasi — implisit di sini aman karena formatnya baku dan tidak ambigu.
Konversi Teks ke Tanggal #
CAST(teks AS DATE) berhasil bila teksnya mengikuti format tanggal yang dikenali MySQL. Format ISO '2026-01-15' pasti diterima, dan MySQL masih bersikap longgar pada variasi jumlah digit:
SELECT CAST('2026-01-15' AS DATE) AS format_baku,
CAST('2026-1-5' AS DATE) AS format_longgar;Output yang Dihasilkan:
+-------------+----------------+
| format_baku | format_longgar |
+-------------+----------------+
| 2026-01-15 | 2026-01-05 |
+-------------+----------------+
1 row in set (0.00 sec)Garis batasnya: format teks yang menyimpang jauh, misalnya menyebut nama bulan, tidak lagi bisa dikonversi dengan CAST:
SELECT CAST('15 Januari 2026' AS DATE) AS tanggal_tidak_baku;
SHOW WARNINGS;Output yang Dihasilkan:
+--------------------+
| tanggal_tidak_baku |
+--------------------+
| NULL |
+--------------------+
1 row in set, 1 warning (0.00 sec)
+---------+------+---------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------+
| Warning | 1292 | Incorrect datetime value: '15 Januari 2026' |
+---------+------+---------------------------------------------+
1 row in set (0.00 sec)Hasilnya NULL plus peringatan 1292 — CAST menolak teks tersebut tanpa membatalkan query. Solusi untuk teks non-baku adalah fungsi STR_TO_DATE(teks, pola) yang membaca teks sesuai pola DATE_FORMAT:
SELECT STR_TO_DATE('15 January 2026', '%d %M %Y') AS dari_teks_inggris;Output yang Dihasilkan:
+-------------------+
| dari_teks_inggris |
+-------------------+
| 2026-01-15 |
+-------------------+
1 row in set (0.00 sec)Fungsi STR_TO_DATE mengembalikan DATE yang sah dari teks '15 January 2026'. Satu batasan yang perlu diketahui: nama bulan dan hari dalam pola %M/%W dibaca dalam bahasa Inggris — teks '15 Januari 2026' tetap menghasilkan NULL walaupun kita sudah mengatur lc_time_names ke id_ID, karena pengaturan itu hanya mengatur tampilan, bukan parsing.
Membersihkan Database Latihan #
DROP DATABASE belajar_fungsi;Output yang Dihasilkan:
Query OK, 1 row affected (0.00 sec)Konversi tipe menutup satu celah kesalahan, tetapi ada satu “tipe” yang mengubah aturan main di hampir semua operasi: NULL. Nilai tak-diketahui ini menjalar lewat operasi matematika, menipu fungsi COUNT, dan menuntut pembanding khusus IS NULL. Chapter berikutnya membahasnya secara tuntas bersama IFNULL, COALESCE, dan NULLIF.