Data hampir selalu tiba sebagai teks: baris dari berkas CSV, payload JSON dari aplikasi, input pengguna di formulir web. Di sisi lain, kolom database yang sehat bertipe integer, date, atau numeric — bukan text — agar bisa dihitung, dibandingkan, dan diindeks dengan benar. Di antara kedua dunia itu berdiri konversi tipe: mengubah nilai dari satu tipe ke tipe lain secara eksplisit dan terkendali. PostgreSQL menyediakan sintaks standar SQL CAST(nilai AS tipe) serta singkatannya nilai::tipe, dan keduanya datang dengan satu janji penting: konversi yang tidak mungkin akan gagal keras, bukan menghasilkan sampah senyap. Chapter ini memperkenalkan kedua sintaks itu, konversi yang paling sering dibutuhkan sehari-hari, pola aman saat menangani data impor, dan gambaran besar konversi implisit versus eksplisit.
Sintaks Standar: CAST(nilai AS tipe) #
Sintaks CAST berasal dari standar SQL dan bekerja di hampir semua database relasional. Empat konversi berikut adalah yang paling sering dipakai dalam praktik:
| Konversi | Contoh | Hasil | Catatan |
|---|---|---|---|
| text → integer | CAST('42' AS integer) | 42 | String harus berupa angka yang sah |
| text → date | CAST('2026-01-15' AS date) | 2026-01-15 | Format ISO paling aman |
| integer → text | CAST(5000 AS text) | 5000 | Sering untuk merangkai teks |
| timestamp → date | CAST(TIMESTAMP '...' AS date) | tanggal polos | Komponen jam dibuang |
-- konversi teks menjadi angka
SELECT CAST('42' AS integer) AS angka,
pg_typeof(CAST('42' AS integer)) AS tipe_hasil;
-- konversi teks menjadi tanggal
SELECT CAST('2026-01-15' AS date) AS tanggal;
-- konversi angka menjadi teks
SELECT CAST(5000 AS text) AS teks;
-- konversi timestamp menjadi date: komponen jam dibuang
SELECT CAST(TIMESTAMP '2026-01-15 08:30:00' AS date) AS tanpa_jam;Output yang Dihasilkan:
angka | tipe_hasil
-------+------------
42 | integer
(1 row)
tanggal
------------
2026-01-15
(1 row)
teks
------
5000
(1 row)
tanpa_jam
------------
2026-01-15
(1 row)Kolom tipe_hasil membuktikan lewat fungsi pg_typeof() bahwa hasil konversi benar-benar bertipe integer, bukan teks yang hanya tampak seperti angka. Perhatikan juga konversi timestamp ke date: yang terjadi adalah pemotongan, bukan pembulatan — jam 08:30:00 dibuang dan tanggal 2026-01-15 yang tersisa. Satu pengecualian yang sering ditanyakan: CAST(NULL AS integer) tidak error; NULL bisa “menjadi” tipe apa pun karena NULL memang nilai tanpa tipe yang menempel.
Notasi Ringkas PostgreSQL: nilai::tipe #
PostgreSQL menambahkan notasi historis :: yang melakukan hal persis sama dengan CAST, tetapi lebih ringkas — terutama saat cast perlu disisipkan ke ekspresi panjang:
| Sintaks | Asal | Contoh |
|---|---|---|
CAST(x AS tipe) | Standar SQL, portabel antar database | CAST('42' AS integer) |
x::tipe | Khusus PostgreSQL, lebih ringkas | '42'::integer |
-- notasi :: adalah singkatan khusus PostgreSQL untuk CAST
SELECT '42'::integer AS angka,
'2026-01-15'::date AS tanggal,
5000::text AS teks,
TIMESTAMP '2026-01-15 08:30:00'::date AS tanpa_jam;
-- hasilnya identik dengan CAST standar
SELECT CAST('42' AS integer) = '42'::integer AS sama_hasilnya;
-- cast bisa dipakai langsung di dalam ekspresi
SELECT 100 + '58'::integer AS jumlah;Output yang Dihasilkan:
angka | tanggal | teks | tanpa_jam
-------+------------+------+------------
42 | 2026-01-15 | 5000 | 2026-01-15
(1 row)
sama_hasilnya
---------------
t
(1 row)
jumlah
--------
158
(1 row)Query kedua membuktikan kesetaraan kedua sintaks: perbandingan CAST('42' AS integer) = '42'::integer menghasilkan true. Query ketiga memperlihatkan alasan notasi :: begitu populer — '58'::integer menempel rapat pada nilainya, sehingga ekspresi campuran seperti 100 + '58'::integer tetap mudah dibaca. Anda sudah melihat notasi ini berkeliaran di chapter-chapter sebelumnya (misalnya id::text saat padding kode di chapter Fungsi String); mulai sekarang kita tahu persis apa artinya.
Ketika Konversi Gagal #
Konversi antar tipe bukan terjemahan bebas — server harus bisa membaca nilai sumber sebagai representasi yang sah untuk tipe tujuan. Ketika tidak bisa, PostgreSQL menolak dengan error, bukan menebak-nebak:
-- string yang tidak mewakili angka ditolak mentah-mentah
SELECT CAST('empat puluh dua' AS integer) AS gagal;Output yang Dihasilkan:
psql:cast-gagal.sql:2: ERROR: invalid input syntax for type integer: "empat puluh dua"
LINE 1: SELECT CAST('empat puluh dua' AS integer) AS gagal;
^Error invalid input syntax for type integer menyebut tipe tujuan dan nilai yang bermasalah — 'empat puluh dua' bukan representasi angka yang dikenal parser. Error serupa muncul untuk tipe lain: invalid input syntax for type date untuk tanggal karangan, dan invalid input syntax for type json untuk teks yang bukan JSON valid. Kegagalan terjadi pada saat konversi berjalan, sehingga satu baris data kotor di tengah INSERT ... SELECT besar bisa membatalkan seluruh pernyataan — perilaku yang melelahkan sekaligus menyelamatkan: Anda tidak akan pernah diam-diam menyimpan "empat" di kolom integer.
Pola Aman: Konversi Saat Membaca #
Kasus nyata konversi tipe paling sering ditemui pada tabel hasil impor, ketika seluruh kolom masih bertipe text karena begitulah isi berkas CSV. Pola yang sehat: konversi pada saat membaca sambil merencanakan migrasi kolom ke tipe yang benar:
-- tabel hasil impor berkas CSV: semua kolom masih bertipe text
CREATE TABLE pelanggan_csv (
nama text NOT NULL,
poin_teks text NOT NULL,
lahir_teks text NOT NULL
);
INSERT INTO pelanggan_csv (nama, poin_teks, lahir_teks) VALUES
('Budi', '120', '1990-05-20'),
('Siti', '85', '1995-11-02'),
('Toko Maju Jaya', '240', '1988-03-17');
-- konversi saat dibaca: poin bisa dihitung, tanggal bisa dibandingkan
SELECT nama,
poin_teks::integer AS poin,
lahir_teks::date AS tanggal_lahir,
poin_teks::integer * 10 AS poin_bonus
FROM pelanggan_csv
ORDER BY nama;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 3
nama | poin | tanggal_lahir | poin_bonus
----------------+------+---------------+------------
Budi | 120 | 1990-05-20 | 1200
Siti | 85 | 1995-11-02 | 850
Toko Maju Jaya | 240 | 1988-03-17 | 2400
(3 rows)Karena poin_teks::integer sudah menjadi angka, kolom turunan poin_bonus yang mengalikan nilainya dengan 10 langsung sah — tanpa cast, poin_teks * 10 akan ditolak karena tidak ada operator text * integer. Pola konversi-saat-baca ini cocok untuk data tahap eksplorasi; untuk data yang akan dipakai jangka panjang, langkah penutupnya adalah membuat tabel bertipe benar (integer, date) dan memindahkan data lewat INSERT INTO ... SELECT ... ::integer — atau mengubah tipe kolom langsung dengan ALTER TABLE ... USING, yang dibahas mendalam di chapter Type Casting
.
Konversi Implisit vs Eksplisit #
Sejauh ini kita menyebut cast secara eksplisit — Anda menuliskan konversinya sendiri. PostgreSQL juga melakukan sejumlah konversi implisit, dan batas antara keduanya penting dipahami agar tidak kaget. Literal berkutip tanpa anotasi tipe seperti '120' berstatus “unknown” dan ditafsirkan mengikuti konteksnya:
-- literal tanpa tipe ('120') otomatis ditafsirkan mengikuti konteks kolom
CREATE TABLE poin_member (
id integer PRIMARY KEY,
poin integer NOT NULL,
gabung date NOT NULL
);
INSERT INTO poin_member (id, poin, gabung) VALUES (1, '120', '2026-01-15');
SELECT id, poin, gabung FROM poin_member ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 1
id | poin | gabung
----+------+------------
1 | 120 | 2026-01-15
(1 row)INSERT di atas berhasil tanpa satu pun CAST — literal '120' diterima kolom integer dan '2026-01-15' diterima kolom date, karena keduanya berstatus unknown sehingga resolver tipe bebas menafsirkannya sebagai integer dan date. Namun kemudahan ini hanya berlaku untuk literal unknown. Begitu sebuah nilai sudah terikat bertipe text, PostgreSQL menolak memasukkannya ke kolom integer tanpa cast eksplisit:
-- nilai yang sudah bertipe text tidak dikonversi secara diam-diam
INSERT INTO poin_member (id, poin, gabung) VALUES (2, '85'::text, '2025-08-10');Output yang Dihasilkan:
psql:implisit-gagal.sql:2: ERROR: column "poin" is of type integer but expression is of type text
LINE 1: ...RT INTO poin_member (id, poin, gabung) VALUES (2, '85'::text...
^
HINT: You will need to rewrite or cast the expression.Error column "poin" is of type integer but expression is of type text menegaskan garis batasnya: unknown literal ditafsirkan, nilai text sungguhan ditolak. Aturan main lengkapnya — konteks implisit, assignment, dan eksplisit yang tersimpan di katalog sistem, lengkap dengan notasi fungsi, cast ke domain, dan jebakan konversi diam-diam — dibahas tuntas di chapter Type Casting
di ujung section ini.
Konversi tipe menuntaskan rangkaian “mengubah wujud data”. Topik berikutnya menangani wujud data yang paling licin di database: NULL — nilai yang bahkan tidak sama dengan dirinya sendiri. Fungsi COALESCE, NULLIF, dan teman-temannya menunggu di chapter Fungsi NULL
.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
cast-dasar.sql
Unduh berkas-- konversi teks menjadi angka
SELECT CAST('42' AS integer) AS angka,
pg_typeof(CAST('42' AS integer)) AS tipe_hasil;
-- konversi teks menjadi tanggal
SELECT CAST('2026-01-15' AS date) AS tanggal;
-- konversi angka menjadi teks
SELECT CAST(5000 AS text) AS teks;
-- konversi timestamp menjadi date: komponen jam dibuang
SELECT CAST(TIMESTAMP '2026-01-15 08:30:00' AS date) AS tanpa_jam;
cast-gagal.sql
Unduh berkas-- string yang tidak mewakili angka ditolak mentah-mentah
SELECT CAST('empat puluh dua' AS integer) AS gagal;
cast-shorthand.sql
Unduh berkas-- notasi :: adalah singkatan khusus PostgreSQL untuk CAST
SELECT '42'::integer AS angka,
'2026-01-15'::date AS tanggal,
5000::text AS teks,
TIMESTAMP '2026-01-15 08:30:00'::date AS tanpa_jam;
-- hasilnya identik dengan CAST standar
SELECT CAST('42' AS integer) = '42'::integer AS sama_hasilnya;
-- cast bisa dipakai langsung di dalam ekspresi
SELECT 100 + '58'::integer AS jumlah;
implisit-gagal.sql
Unduh berkas-- nilai yang sudah bertipe text tidak dikonversi secara diam-diam
INSERT INTO poin_member (id, poin, gabung) VALUES (2, '85'::text, '2025-08-10');
implisit-vs-eksplisit.sql
Unduh berkas-- literal tanpa tipe ('120') otomatis ditafsirkan mengikuti konteks kolom
CREATE TABLE poin_member (
id integer PRIMARY KEY,
poin integer NOT NULL,
gabung date NOT NULL
);
INSERT INTO poin_member (id, poin, gabung) VALUES (1, '120', '2026-01-15');
SELECT id, poin, gabung FROM poin_member ORDER BY id;
konversi-konteks.sql
Unduh berkas-- tabel hasil impor berkas CSV: semua kolom masih bertipe text
CREATE TABLE pelanggan_csv (
nama text NOT NULL,
poin_teks text NOT NULL,
lahir_teks text NOT NULL
);
INSERT INTO pelanggan_csv (nama, poin_teks, lahir_teks) VALUES
('Budi', '120', '1990-05-20'),
('Siti', '85', '1995-11-02'),
('Toko Maju Jaya', '240', '1988-03-17');
-- konversi saat dibaca: poin bisa dihitung, tanggal bisa dibandingkan
SELECT nama,
poin_teks::integer AS poin,
lahir_teks::date AS tanggal_lahir,
poin_teks::integer * 10 AS poin_bonus
FROM pelanggan_csv
ORDER BY nama;