Nama pelanggan, alamat, catatan pesanan — mayoritas kolom di aplikasi dunia nyata menyimpan teks, dan PostgreSQL menyediakan tiga tipe untuknya: char(n), varchar(n), dan text. Warisan database lain membuat banyak orang memilih dengan asumsi yang keliru: bahwa char lebih cepat untuk kode pendek, atau varchar lebih hemat dari text. Kenyataannya di PostgreSQL jauh lebih membosankan sekaligus membebaskan — ketiganya disimpan dengan struktur yang sama dan sama cepatnya, sehingga pilihan sebenarnya hanya soal perilaku, bukan performa. Chapter ini membuktikannya sambil membedah tiga perilaku khas tiap tipe: spasi pengisi milik char, batas panjang milik varchar, dan kebebasan tanpa batas milik text.
Tiga Cara Menyimpan Teks #
Ketiga tipe ini adalah satu keluarga dengan satu perbedaan utama — apa yang terjadi pada panjang nilai:
| Tipe | Batas panjang | Perilaku terhadap panjang |
|---|---|---|
char(n) | Tetap tepat n karakter (1-10485760) | Nilai lebih pendek diisi spasi sampai n; nilai lebih panjang ditolak |
varchar(n) | Maksimal n karakter (1-10485760) | Nilai disimpan apa adanya; nilai lebih panjang dari n ditolak |
text | Tanpa batas (sampai 1 GB per nilai) | Nilai disimpan apa adanya |
Angka n pada char(n) dan varchar(n) menghitung karakter, bukan byte — huruf beraksen seperti é dihitung satu karakter meski memakan dua byte dalam UTF-8. char tanpa (n) setara char(1); varchar tanpa (n) setara text.
Membuat Tabel dengan char, varchar, dan text #
Tabel keanggotaan berikut memakai ketiganya sekaligus dalam peran yang wajar: char(5) untuk kode berformat tetap, varchar(20) untuk nama yang wajar dibatasi, dan text untuk alamat yang panjangnya tidak terduga:
CREATE TABLE anggota (
id integer PRIMARY KEY,
kode char(5) NOT NULL,
nama varchar(20) NOT NULL,
alamat text
);
INSERT INTO anggota (id, kode, nama, alamat) VALUES
(1, 'ID001', 'Budi', 'Jl. Merdeka No. 10, Jakarta'),
(2, 'ID002', 'Siti', 'Jl. Asia Afrika No. 5, Bandung'),
(3, 'ID003', 'Toko Maju Jaya', 'Jl. Sudirman Kav. 52, Jakarta');
SELECT id, kode, length(kode) AS panjang_kode, nama, alamat
FROM anggota
ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 3
id | kode | panjang_kode | nama | alamat
----+-------+--------------+----------------+--------------------------------
1 | ID001 | 5 | Budi | Jl. Merdeka No. 10, Jakarta
2 | ID002 | 5 | Siti | Jl. Asia Afrika No. 5, Bandung
3 | ID003 | 5 | Toko Maju Jaya | Jl. Sudirman Kav. 52, Jakarta
(3 rows)Ketiga kode anggota tepat lima karakter, jadi kolom char(5) menyimpannya tanpa perubahan dan length(kode) melaporkan 5 untuk semua baris. Nama terpanjang (“Toko Maju Jaya”, 14 karakter) masih longgar di bawah batas 20 milik varchar(20), dan alamat terpanjang disimpan utuh oleh text tanpa perlu kita berpikir panjang.
char(n) Menambah Spasi Pengisi #
Perilaku khas char(n) baru terlihat saat nilainya lebih pendek dari n. SQL standar memerintahkan char(n) mengisi kekurangan panjang dengan spasi, dan PostgreSQL mematuhinya — spasi itu benar-benar tersimpan di disk:
SELECT 'ID'::char(5) AS kode_char,
length('ID'::char(5)) AS jumlah_karakter,
octet_length('ID'::char(5)) AS jumlah_byte;
SELECT 'ID'::varchar(5) AS kode_varchar,
length('ID'::varchar(5)) AS jumlah_karakter,
octet_length('ID'::varchar(5)) AS jumlah_byte;
SELECT 'ID'::char(5) = 'ID' AS dibanding_tanpa_spasi;Output yang Dihasilkan:
kode_char | jumlah_karakter | jumlah_byte
-----------+-----------------+-------------
ID | 2 | 5
(1 row)
kode_varchar | jumlah_karakter | jumlah_byte
--------------+-----------------+-------------
ID | 2 | 2
(1 row)
dibanding_tanpa_spasi
-----------------------
t
(1 row)Mari kita bedah hasil yang menarik ini:
- Baris pertama:
'ID'::char(5)hanya punya dua karakter berarti, tetapioctet_lengthmelaporkan5— tiga spasi pengisi ikut tersimpan. Fungsilengthsendiri melaporkan2karena untuk tipechar, fungsi pengukur karakter mengabaikan spasi tambahan di belakang. - Baris kedua:
varchar(5)menyimpan'ID'apa adanya — dua karakter, dua byte, tidak ada spasi pengisi di mana pun. - Baris ketiga: dalam perbandingan
chardenganchar, spasi belakang dianggap tidak signifikan —'ID'::char(5)dianggap sama dengan'ID', karena keduanya dibandingkan sebagaichardengan aturan SQL.
Jadi spasi pengisi char(n) itu nyata di penyimpanan tetapi hampir tak terlihat di permukaan: tampilan menyembunyikannya (psql memotong spasi belakang saat menampilkan char), length mengabaikannya, dan perbandingan mengampuninya. Satu-satunya tempat ia menggigit adalah saat nilainya keluar dari dunia char — misalnya ditempel ke text — karena cast char ke text membuang spasi pengisinya, atau saat menghitung ukuran baris: tabel besar dengan char(50) membayar penuh 50 karakter untuk setiap baris pendek.
varchar(n) Menolak Teks yang Terlalu Panjang #
Batas n pada varchar ditegakkan sungguhan. Nama anggota keempat kita, “Alexander Wolfgang Gunawan”, terdiri dari 26 karakter — melewati batas 20 milik varchar(20):
INSERT INTO anggota (id, kode, nama, alamat)
VALUES (4, 'ID004', 'Alexander Wolfgang Gunawan', 'Solo');Output yang Dihasilkan:
psql:varchar-overflow.sql:2: ERROR: value too long for type character varying(20)Error value too long for type character varying(20) menyebutkan tipe dan batasnya dengan jelas, dan seluruh baris ditolak — kolom lain yang sah ikut tidak tersimpan. Batas seperti ini berguna sebagai penjaga integritas (nama kolom email jarang lebih dari 100 karakter), tetapi pilih n dengan longgar: memperbesar batas di kemudian hari lewat ALTER TABLE mudah, sedangkan baris yang sudah ditolak aplikasi tidak kembali dengan sendirinya.
Cast Eksplisit Memotong Diam-diam #
Ada satu celah di mana teks terlalu panjang justru lolos: cast eksplisit ke char(n) atau varchar(n) tidak menolak, melainkan memotong nilai sampai pas n karakter:
SELECT 'Budi Santoso'::char(4) AS potong_char,
'Budi Santoso'::varchar(4) AS potong_varchar;
SELECT length('Budi Santoso'::char(4)) AS panjang_char,
length('Budi Santoso'::varchar(4)) AS panjang_varchar;Output yang Dihasilkan:
potong_char | potong_varchar
-------------+----------------
Budi | Budi
(1 row)
panjang_char | panjang_varchar
--------------+-----------------
4 | 4
(1 row)Dua belas karakter masuk, empat karakter keluar: 'Budi Santoso' menjadi 'Budi' tanpa error, tanpa warning, tanpa sisa. Perilaku potong pada cast eksplisit ini memang disediakan standar SQL untuk keperluan pemotongan yang sengaja diinginkan — tapi kebalikannya berbahaya: CAST(nama AS varchar(20)) di kode aplikasi bisa memotong nama pelanggan tanpa pernah terdengar. INSERT ke kolom tetap menolak; hanya cast eksplisit yang memotong.
Mitos Performa: Ketiganya Sama Cepat #
Mitos paling awet di dunia SQL: “char lebih cepat untuk nilai pendek” atau “text lebih lambat dari varchar”. Di PostgreSQL, itu semua tidak benar — dokumentasi resminya menegaskan bahwa ketiganya memakai struktur penyimpanan yang persis sama, dan tidak ada perbedaan performa yang terukur:
| Klaim yang beredar | Kenyataan di PostgreSQL |
|---|---|
char(n) lebih cepat untuk kode pendek | Justru menambah spasi pengisi yang ikut tersimpan dan dibandingkan |
varchar lebih cepat dari text pada nilai panjang | Identik — varchar tanpa batas adalah text |
text boros untuk nilai pendek | Struktur penyimpanan sama; panjang nilai yang menentukan |
Alasan historisnya: beberapa database lawas memang menyimpan char dan varchar dengan format berbeda. PostgreSQL tidak — sejak lama ketiganya memakai varlena, struktur panjang-variabel yang sama, sehingga n pada varchar(n) murni berfungsi sebagai constraint, bukan optimasi.
Teks selesai disimpan apa adanya, sekarang kita berpindah ke tipe paling ringkas di katalog: boolean — dua nilai, tiga kemungkinan, dan jebakan logikanya yang khas.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
buat-tabel.sql
Unduh berkasCREATE TABLE anggota (
id integer PRIMARY KEY,
kode char(5) NOT NULL,
nama varchar(20) NOT NULL,
alamat text
);
INSERT INTO anggota (id, kode, nama, alamat) VALUES
(1, 'ID001', 'Budi', 'Jl. Merdeka No. 10, Jakarta'),
(2, 'ID002', 'Siti', 'Jl. Asia Afrika No. 5, Bandung'),
(3, 'ID003', 'Toko Maju Jaya', 'Jl. Sudirman Kav. 52, Jakarta');
SELECT id, kode, length(kode) AS panjang_kode, nama, alamat
FROM anggota
ORDER BY id;
padding-char.sql
Unduh berkasSELECT 'ID'::char(5) AS kode_char,
length('ID'::char(5)) AS jumlah_karakter,
octet_length('ID'::char(5)) AS jumlah_byte;
SELECT 'ID'::varchar(5) AS kode_varchar,
length('ID'::varchar(5)) AS jumlah_karakter,
octet_length('ID'::varchar(5)) AS jumlah_byte;
SELECT 'ID'::char(5) = 'ID' AS dibanding_tanpa_spasi;
potong-cast.sql
Unduh berkasSELECT 'Budi Santoso'::char(4) AS potong_char,
'Budi Santoso'::varchar(4) AS potong_varchar;
SELECT length('Budi Santoso'::char(4)) AS panjang_char,
length('Budi Santoso'::varchar(4)) AS panjang_varchar;
varchar-overflow.sql
Unduh berkasINSERT INTO anggota (id, kode, nama, alamat)
VALUES (4, 'ID004', 'Alexander Wolfgang Gunawan', 'Solo');