Chapter sebelumnya memperlihatkan bagaimana LIKE mencocokkan pola teks dengan wildcard % dan _. Dua wildcard itu cukup untuk pencarian sederhana, tetapi cepat kewalahan saat polanya menuntut presisi: nomor telepon yang boleh memakai strip, spasi, atau kode negara +62; domain email yang harus dipilih dari beberapa alternatif; atau pencarian kata dana yang tidak boleh ikut mencocokkan kata perdana. Untuk kebutuhan seperti itu, PostgreSQL menyediakan regular expression (regex) berbasis standar POSIX — bahasa pola yang jauh lebih ekspresif dan dipakai hampir di semua bahasa pemrograman. Pada chapter ini kita pelajari empat operator regex, anatomi polanya, word boundary untuk pencarian kata utuh, lalu function regexp_* untuk mengubah dan mengekstrak teks.

Menyiapkan Tabel Latihan #

Kita pakai tabel kontak berisi delapan pelanggan Toko Maju Jaya dengan format nomor telepon yang sengaja dibuat beragam, ditambah tiga varian domain email yang berbeda:

sql buat-tabel.sql
CREATE TABLE kontak (
    id      integer PRIMARY KEY,
    nama    text NOT NULL,
    email   text NOT NULL,
    telepon text NOT NULL
);

INSERT INTO kontak (id, nama, email, telepon) VALUES
    (1, 'Budi Santoso',  '[email protected]',           '0812-3456-7890'),
    (2, 'Siti Rahayu',   '[email protected]',           '0813 4455 6677'),
    (3, 'Andi Wijaya',   '[email protected]',           '082156789012'),
    (4, 'Dewi Lestari',  '[email protected]',           '0815-9988-7766'),
    (5, 'Rudi Hartono',  '[email protected]',   '0877123456'),
    (6, 'Maya Putri',    '[email protected]',           '0812-3456'),
    (7, 'Agus Salim',    '[email protected]',           '+62 812-9999-8888'),
    (8, 'Rina Melati',   '[email protected]',           '0215551234');

Output yang Dihasilkan:

text
CREATE TABLE
INSERT 0 8

Kolom telepon menjadi bahan latihan utama kita: ada nomor dengan pemisah strip (0812-3456-7890), dengan spasi (0813 4455 6677), tanpa pemisah sama sekali (082156789012), berprefiks kode negara (+62 812-9999-8888), dan satu nomor telepon rumah 0215551234. Pola semacam ini mustahil dibedakan hanya dengan % dan _ tanpa menulis banyak kombinasi OR.

Empat Operator Regex PostgreSQL #

PostgreSQL tidak memakai function untuk mencocokkan regex, melainkan empat operator yang ditempelkan langsung di klausa WHERE. Nilai di sisi kiri adalah teks yang diperiksa; nilai di sisi kanan adalah polanya:

OperatorMaknaContoh
~cocok, peka huruf besar/keciltelepon ~ '^08'
~*cocok, abaikan huruf besar/kecilnama ~* '^b'
!~tidak cocok, peka huruf besar/keciltelepon !~ '-'
!~*tidak cocok, abaikan huruf besar/kecilemail !~* 'example\.com'
-- Operator ~ : cocok, case-sensitive.
-- Cari semua kontak dengan email di domain example.com.
SELECT nama, email
FROM kontak
WHERE email ~ '@example\.com$'
ORDER BY id;

-- Operator ~* : cocok, mengabaikan huruf besar/kecil.
-- Pola 'b' cocok dengan 'B' maupun 'b' pada kolom nama.
SELECT nama
FROM kontak
WHERE nama ~* '^b'
ORDER BY id;

-- Operator !~ : TIDAK cocok, case-sensitive.
-- Nomor telepon yang sama sekali tidak memuat tanda hubung.
SELECT nama, telepon
FROM kontak
WHERE telepon !~ '-'
ORDER BY id;

-- Operator !~* : TIDAK cocok, mengabaikan huruf besar/kecil.
-- Email yang tidak menyebut kata "example" pada domainnya.
SELECT nama, email
FROM kontak
WHERE email !~* 'example\.com'
ORDER BY id;

Output yang Dihasilkan:

text
     nama     |          email           
--------------+--------------------------
 Budi Santoso | [email protected]
 Siti Rahayu  | [email protected]
 Dewi Lestari | [email protected]
 Rudi Hartono | [email protected]
 Agus Salim   | [email protected]
 Rina Melati  | [email protected]
(6 rows)

     nama     
--------------
 Budi Santoso
(1 row)

     nama     |    telepon     
--------------+----------------
 Siti Rahayu  | 0813 4455 6677
 Andi Wijaya  | 082156789012
 Rudi Hartono | 0877123456
 Rina Melati  | 0215551234
(4 rows)

    nama     |      email       
-------------+------------------
 Andi Wijaya | [email protected]
 Maya Putri  | [email protected]
(2 rows)

Mari kita bedah keempat query di atas:

  1. email ~ '@example\.com$' — pola berisi @example.com yang diakhiri tanda $ (akhir string). Titik pada .com ditulis \. agar dimaknai titik literal, bukan “karakter apa pun”. Query pertama mengembalikan enam kontak berdomain example.com.
  2. nama ~* '^b' — operator ~* mengabaikan kapitalisasi, sehingga pola huruf kecil b tetap cocok dengan Budi Santoso yang diawali huruf besar. Dengan operator ~, query ini akan mengembalikan nol baris.
  3. telepon !~ '-' — negasi dari ~: hanya baris yang polanya tidak ditemukan yang lolos. Hasilnya empat nomor tanpa strip: format spasi, format murni digit, dan nomor telepon rumah.
  4. email !~* 'example\.com' — negasi tanpa memedulikan huruf besar/kecil; dua kontak dengan domain example.net dan example.org yang tersisa.

Anatomi Pola: Anchor, Kelas Karakter, dan Kuantifier #

Sebuah pola regex tersusun dari elemen-elemen kecil yang saling dirangkai. Tabel berikut merangkum elemen yang paling sering dipakai sehari-hari:

ElemenMaknaContoh polaCocok dengan
^awal string^080812..., 0877...
$akhir string\.com$semua teks berakhir .com
.satu karakter apa pun08.10811, 08-1, 08 1
[abc]satu karakter dari himpunan[0-9]satu digit
[a-z]satu karakter dari rentang[a-z]satu huruf kecil
+repetisi 1 kali atau lebih[0-9]+7, 123
*repetisi 0 kali atau lebih[0-9]*string kosong, 123
?repetisi 0 atau 1 kali-?[0-9]+12, -12
{n}repetisi tepat n kali[0-9]{4}1234
{n,m}repetisi n sampai m kali[0-9]{8,12}8 sampai 12 digit
|alternatif (atau)com|netcom, net
( )pengelompokan(ab)+ab, abab

Mari kita terapkan elemen-elemen itu pada data telepon dan email yang beragam tadi:

sql pola-dasar.sql
-- Anchor ^ : pola harus cocok di AWAL string.
-- Semua nomor seluler Indonesia diawali kode 08.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08'
ORDER BY id;

-- Anchor $ : pola harus cocok di AKHIR string.
-- Semua email yang berakhir dengan domain .com.
SELECT nama, email
FROM kontak
WHERE email ~ '\.com$'
ORDER BY id;

-- Titik . berarti "satu karakter apa pun", kelas [0-9] berarti "satu digit".
-- Nomor dengan format 08, dua karakter bebas, lalu tanda hubung.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08..-'
ORDER BY id;

-- Kuantifier {8,12} : pengulangan rentang.
-- Nomor seluler murni digit: 08 diikuti 8 sampai 12 digit, tanpa pemisah.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08[0-9]{8,12}$'
ORDER BY id;

-- Pengelompokan ( ) dan pengulangan grup + :
-- 08, lalu satu atau beberapa blok "digit lalu strip", lalu digit penutup.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08([0-9]+-)+[0-9]+$'
ORDER BY id;

-- Alternatif | : salah satu dari beberapa pilihan.
-- Email di domain example.com ATAU example.net.
SELECT nama, email
FROM kontak
WHERE email ~ '@(example\.com|example\.net)$'
ORDER BY id;

-- Tanda tanya ? : elemen sebelumnya opsional (0 atau 1 kali).
-- Nomor format internasional: tanda + boleh ada, boleh tidak.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^\+?62'
ORDER BY id;

Output yang Dihasilkan:

text
     nama     |    telepon     
--------------+----------------
 Budi Santoso | 0812-3456-7890
 Siti Rahayu  | 0813 4455 6677
 Andi Wijaya  | 082156789012
 Dewi Lestari | 0815-9988-7766
 Rudi Hartono | 0877123456
 Maya Putri   | 0812-3456
(6 rows)

     nama     |          email           
--------------+--------------------------
 Budi Santoso | [email protected]
 Siti Rahayu  | [email protected]
 Dewi Lestari | [email protected]
 Rudi Hartono | [email protected]
 Agus Salim   | [email protected]
 Rina Melati  | [email protected]
(6 rows)

     nama     |    telepon     
--------------+----------------
 Budi Santoso | 0812-3456-7890
 Dewi Lestari | 0815-9988-7766
 Maya Putri   | 0812-3456
(3 rows)

     nama     |   telepon    
--------------+--------------
 Andi Wijaya  | 082156789012
 Rudi Hartono | 0877123456
(2 rows)

     nama     |    telepon     
--------------+----------------
 Budi Santoso | 0812-3456-7890
 Dewi Lestari | 0815-9988-7766
 Maya Putri   | 0812-3456
(3 rows)

     nama     |          email           
--------------+--------------------------
 Budi Santoso | [email protected]
 Siti Rahayu  | [email protected]
 Andi Wijaya  | [email protected]
 Dewi Lestari | [email protected]
 Rudi Hartono | [email protected]
 Agus Salim   | [email protected]
 Rina Melati  | [email protected]
(7 rows)

    nama    |      telepon      
------------+-------------------
 Agus Salim | +62 812-9999-8888
(1 row)

Tiga keputusan pola pada contoh di atas layak dicermati lebih dekat:

  1. '^08' vs '^08[0-9]{8,12}$' — pola pertama hanya memeriksa awal string, sehingga enam nomor berawalan 08 lolos, termasuk 0812-3456 yang pendek. Pola kedua dijepit anchor ^ dan $ sekaligus, memaksa seluruh string cocok penuh: hanya dua nomor murni digit dengan panjang yang sah yang lolos.
  2. '^08([0-9]+-)+[0-9]+$' — tanda kurung mengelompokkan [0-9]+- menjadi satu blok, dan + di luar kurung mengulang blok itu. Pola ini menerima 0812-3456 (satu blok) sekaligus 0812-3456-7890 (dua blok) — sesuatu yang tidak bisa dinyatakan LIKE dengan satu pola.
  3. '@(example\.com|example\.net)$' — alternatif berada di dalam tanda kurung; tanpa kurung, $ hanya akan menempel pada pilihan terakhir dan polanya berubah makna.

Word Boundary: Mencocokkan Kata Utuh #

Bayangkan Anda mencari pengumuman yang membahas dana (dalam arti uang). Masalahnya, kata dana juga menempel di dalam kata perdana. Karena pola biasa mencocokkan bagian mana pun dari string, pola 'dana' ikut meloloskan kalimat berisi perdana. PostgreSQL menyediakan word boundary \y — penanda nol-karakter di batas kata (transisi antara karakter kata dan karakter non-kata) — untuk memaksa pola berdiri sebagai kata utuh:

CREATE TABLE pengumuman (
    id  integer PRIMARY KEY,
    isi text NOT NULL
);

INSERT INTO pengumuman (id, isi) VALUES
    (1, 'Transfer dana talangan toko sudah cair hari Senin.'),
    (2, 'Perdana menteri meresmikan gudang baru bulan depan.'),
    (3, 'Pengiriman tahap kedua dijadwalkan pekan ini.'),
    (4, 'Kedua paket tiba dua hari kemudian.');

-- Tanpa word boundary: pola 'dana' juga menempel di dalam kata 'Perdana'.
SELECT isi
FROM pengumuman
WHERE isi ~ 'dana'
ORDER BY id;

-- Dengan word boundary \y : pola harus berdiri sebagai kata utuh.
SELECT isi
FROM pengumuman
WHERE isi ~ '\ydana\y'
ORDER BY id;

-- 'dua' menempel di dalam 'kedua' — pola tanpa boundary cocok di baris 3 dan 4.
SELECT isi
FROM pengumuman
WHERE isi ~ 'dua'
ORDER BY id;

-- \ydua\y hanya mencocokkan 'dua' yang berdiri sendiri sebagai kata.
SELECT isi
FROM pengumuman
WHERE isi ~ '\ydua\y'
ORDER BY id;

Output yang Dihasilkan:

text
CREATE TABLE
INSERT 0 4
                         isi                         
-----------------------------------------------------
 Transfer dana talangan toko sudah cair hari Senin.
 Perdana menteri meresmikan gudang baru bulan depan.
(2 rows)

                        isi                         
----------------------------------------------------
 Transfer dana talangan toko sudah cair hari Senin.
(1 row)

                      isi                      
-----------------------------------------------
 Pengiriman tahap kedua dijadwalkan pekan ini.
 Kedua paket tiba dua hari kemudian.
(2 rows)

                 isi                 
-------------------------------------
 Kedua paket tiba dua hari kemudian.
(1 row)

Pola 'dana' tanpa boundary mengembalikan dua baris — kalimat berisi kata dana sekaligus kalimat berisi perdana. Menambahkan \y di kedua sisi ('\ydana\y') memangkasnya menjadi satu baris. Pasangan \ydua\y memperlihatkan kasus sebaliknya dari sisi kata yang mengandung: pola 'dua' menangkap kedua dan dua, sedangkan '\ydua\y' hanya menangkap kata dua yang berdiri sendiri. PostgreSQL juga mengenal \m (awal kata) dan \M (akhir kata) bila Anda hanya perlu menjepit satu sisi.

Fungsi Regex: Mengubah dan Mengekstrak Teks #

Operator ~ hanya menjawab “cocok atau tidak”. Untuk memanipulasi teks yang cocok, PostgreSQL menyediakan satu keluarga function: regexp_replace() mengganti kecocokan, substring() dengan klausa FROM mengekstrak kecocokan pertama, dan regexp_matches() mengembalikan setiap kecocokan sebagai baris array:

-- regexp_replace : ganti bagian yang cocok dengan pola.
-- Tanpa flag 'g', hanya kemunculan PERTAMA yang diganti.
SELECT regexp_replace('0812-3456-7890', '[0-9]+', 'X') AS tanpa_flag_g;

-- Dengan flag 'g' (global), semua kemunculan diganti.
SELECT regexp_replace('0812-3456-7890', '[0-9]+', 'X', 'g') AS dengan_flag_g;

-- Normalisasi data: buang semua karakter selain digit dari nomor telepon.
SELECT nama,
       regexp_replace(telepon, '[^0-9]', '', 'g') AS telepon_bersih
FROM kontak
ORDER BY id;

-- substring(kolom FROM pola) : ambil bagian yang cocok dengan pola.
-- Mengambil bagian lokal (sebelum @) dari setiap email.
SELECT nama,
       substring(email FROM '^[a-z.]+') AS nama_lokal
FROM kontak
WHERE email ~ '@example\.com$'
ORDER BY id;

-- regexp_matches : kembalikan setiap kecocokan sebagai baris array.
-- Pola dicari di satu string panjang; flag 'g' mengambil semua kecocokan.
SELECT regexp_matches(
           '[email protected], [email protected], [email protected]',
           '[a-z.]+@[a-z.]+',
           'g'
       ) AS email_ditemukan;

Output yang Dihasilkan:

text
 tanpa_flag_g 
--------------
 X-3456-7890
(1 row)

 dengan_flag_g 
---------------
 X-X-X
(1 row)

     nama     | telepon_bersih 
--------------+----------------
 Budi Santoso | 081234567890
 Siti Rahayu  | 081344556677
 Andi Wijaya  | 082156789012
 Dewi Lestari | 081599887766
 Rudi Hartono | 0877123456
 Maya Putri   | 08123456
 Agus Salim   | 6281299998888
 Rina Melati  | 0215551234
(8 rows)

     nama     |  nama_lokal  
--------------+--------------
 Budi Santoso | budi
 Siti Rahayu  | siti
 Dewi Lestari | dewi
 Rudi Hartono | rudi.hartono
 Agus Salim   | agus
 Rina Melati  | rina
(6 rows)

  email_ditemukan   
--------------------
 {[email protected]}
 {[email protected]}
 {[email protected]}
(3 rows)

Empat perilaku pada contoh di atas perlu digarisbawahi:

  1. Flag 'g' pada regexp_replace() — tanpa flag ini, hanya kecocokan pertama yang diganti (X-3456-7890); dengan flag, semua blok digit diganti (X-X-X).
  2. '[^0-9]' — tanda sirkumfleks di awal kelas karakter berarti negasi: “satu karakter yang bukan digit”. Menggantinya dengan string kosong pada setiap posisi menghasilkan kolom telepon_bersih yang seragam murni digit — contoh nyata regex untuk normalisasi data kotor.
  3. substring(email FROM '^[a-z.]+') — bentuk substring khusus regex ini mengekstrak teks yang cocok dengan pola, bukan posisi indeks. Bila tidak ada kecocokan, hasilnya NULL.
  4. regexp_matches(..., 'g') — function ini mengembalikan setof text[], satu baris array per kecocokan, sehingga cocok dipakai di FROM atau untuk memecah satu kolom menjadi banyak baris.

SIMILAR TO: Sepupu Standar SQL yang Jarang Dipakai #

SQL standar mendefinisikan pola SIMILAR TO — gabungan wildcard LIKE (% dan _) dengan sebagian sintaks regex seperti alternatif dan kuantifier. PostgreSQL mendukungnya, dan untuk pola sederhana hasilnya identik dengan operator ~:

sql similar-to.sql
-- SIMILAR TO: pola gaya SQL standar (wildcard % dan _ ditambah regex sederhana).
SELECT nama
FROM kontak
WHERE nama SIMILAR TO '%(Budi|Siti)%'
ORDER BY id;

-- Operator ~ dengan regex penuh menghasilkan hasil yang sama...
SELECT nama
FROM kontak
WHERE nama ~ 'Budi|Siti'
ORDER BY id;

-- ...tetapi operator ~ jauh lebih kaya: kelas karakter, {n,m}, word boundary,
-- dan kompatibel dengan fungsi regexp_* — alasan regex POSIX menjadi pilihan utama.

Output yang Dihasilkan:

text
     nama     
--------------
 Budi Santoso
 Siti Rahayu
(2 rows)

     nama     
--------------
 Budi Santoso
 Siti Rahayu
(2 rows)

Kedua query mengembalikan Budi Santoso dan Siti Rahayu. Di atas kertas SIMILAR TO lebih portabel antar-database SQL, tetapi ekspresinya lebih terbatas: tidak mengenal word boundary, backreference lengkap, dan sebagian besar fitur POSIX yang kita pakai di chapter ini. Praktik umum komunitas PostgreSQL adalah memilih antara LIKE/ILIKE untuk pola sederhana atau operator ~ untuk pola kompleks — SIMILAR TO nyaris tidak memberikan keunggulan tambahan.

Pola yang Tidak Valid Ditolak Saat Eksekusi #

Regex adalah bahasa kecil di dalam string, sehingga pola yang salah ketik dapat membuat query gagal jalan. Server memeriksa pola pada saat query dieksekusi dan menolaknya dengan pesan error yang cukup jelas:

-- Pola regex yang tidak valid ditolak server saat query dieksekusi:
-- kurung buka '(' tidak pernah ditutup, sehingga polanya tidak seimbang.
SELECT nama
FROM kontak
WHERE nama ~ '('
ORDER BY id;

Output yang Dihasilkan:

text
psql:regex-invalid.sql:6: ERROR:  invalid regular expression: parentheses () not balanced

Error invalid regular expression: parentheses () not balanced muncul karena tanda kurung adalah karakter khusus pengelompokan; satu ( tanpa pasangan ) membuat polanya tidak seimbang. Bila Anda benar-benar ingin mencocokkan karakter kurung literal, escape dengan \( dan \).

Regex memperketat baris mana yang diloloskan klausa WHERE. Namun ada satu kelompok nilai yang lolos dari hampir semua operator perbandingan: NULL. Perbandingan apa pun terhadap NULL menghasilkan NULL, bukan true — dan WHERE hanya meloloskan baris bernilai true. Chapter berikutnya membedah perilaku khusus ini beserta operator penyelamatnya, IS NULL.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

buat-tabel.sql

Unduh berkas
sql
CREATE TABLE kontak (
    id      integer PRIMARY KEY,
    nama    text NOT NULL,
    email   text NOT NULL,
    telepon text NOT NULL
);

INSERT INTO kontak (id, nama, email, telepon) VALUES
    (1, 'Budi Santoso',  '[email protected]',           '0812-3456-7890'),
    (2, 'Siti Rahayu',   '[email protected]',           '0813 4455 6677'),
    (3, 'Andi Wijaya',   '[email protected]',           '082156789012'),
    (4, 'Dewi Lestari',  '[email protected]',           '0815-9988-7766'),
    (5, 'Rudi Hartono',  '[email protected]',   '0877123456'),
    (6, 'Maya Putri',    '[email protected]',           '0812-3456'),
    (7, 'Agus Salim',    '[email protected]',           '+62 812-9999-8888'),
    (8, 'Rina Melati',   '[email protected]',           '0215551234');

fungsi-regex.sql

Unduh berkas
sql
-- regexp_replace : ganti bagian yang cocok dengan pola.
-- Tanpa flag 'g', hanya kemunculan PERTAMA yang diganti.
SELECT regexp_replace('0812-3456-7890', '[0-9]+', 'X') AS tanpa_flag_g;

-- Dengan flag 'g' (global), semua kemunculan diganti.
SELECT regexp_replace('0812-3456-7890', '[0-9]+', 'X', 'g') AS dengan_flag_g;

-- Normalisasi data: buang semua karakter selain digit dari nomor telepon.
SELECT nama,
       regexp_replace(telepon, '[^0-9]', '', 'g') AS telepon_bersih
FROM kontak
ORDER BY id;

-- substring(kolom FROM pola) : ambil bagian yang cocok dengan pola.
-- Mengambil bagian lokal (sebelum @) dari setiap email.
SELECT nama,
       substring(email FROM '^[a-z.]+') AS nama_lokal
FROM kontak
WHERE email ~ '@example\.com$'
ORDER BY id;

-- regexp_matches : kembalikan setiap kecocokan sebagai baris array.
-- Pola dicari di satu string panjang; flag 'g' mengambil semua kecocokan.
SELECT regexp_matches(
           '[email protected], [email protected], [email protected]',
           '[a-z.]+@[a-z.]+',
           'g'
       ) AS email_ditemukan;

operator-dasar.sql

Unduh berkas
sql
-- Operator ~ : cocok, case-sensitive.
-- Cari semua kontak dengan email di domain example.com.
SELECT nama, email
FROM kontak
WHERE email ~ '@example\.com$'
ORDER BY id;

-- Operator ~* : cocok, mengabaikan huruf besar/kecil.
-- Pola 'b' cocok dengan 'B' maupun 'b' pada kolom nama.
SELECT nama
FROM kontak
WHERE nama ~* '^b'
ORDER BY id;

-- Operator !~ : TIDAK cocok, case-sensitive.
-- Nomor telepon yang sama sekali tidak memuat tanda hubung.
SELECT nama, telepon
FROM kontak
WHERE telepon !~ '-'
ORDER BY id;

-- Operator !~* : TIDAK cocok, mengabaikan huruf besar/kecil.
-- Email yang tidak menyebut kata "example" pada domainnya.
SELECT nama, email
FROM kontak
WHERE email !~* 'example\.com'
ORDER BY id;

pola-dasar.sql

Unduh berkas
sql
-- Anchor ^ : pola harus cocok di AWAL string.
-- Semua nomor seluler Indonesia diawali kode 08.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08'
ORDER BY id;

-- Anchor $ : pola harus cocok di AKHIR string.
-- Semua email yang berakhir dengan domain .com.
SELECT nama, email
FROM kontak
WHERE email ~ '\.com$'
ORDER BY id;

-- Titik . berarti "satu karakter apa pun", kelas [0-9] berarti "satu digit".
-- Nomor dengan format 08, dua karakter bebas, lalu tanda hubung.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08..-'
ORDER BY id;

-- Kuantifier {8,12} : pengulangan rentang.
-- Nomor seluler murni digit: 08 diikuti 8 sampai 12 digit, tanpa pemisah.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08[0-9]{8,12}$'
ORDER BY id;

-- Pengelompokan ( ) dan pengulangan grup + :
-- 08, lalu satu atau beberapa blok "digit lalu strip", lalu digit penutup.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^08([0-9]+-)+[0-9]+$'
ORDER BY id;

-- Alternatif | : salah satu dari beberapa pilihan.
-- Email di domain example.com ATAU example.net.
SELECT nama, email
FROM kontak
WHERE email ~ '@(example\.com|example\.net)$'
ORDER BY id;

-- Tanda tanya ? : elemen sebelumnya opsional (0 atau 1 kali).
-- Nomor format internasional: tanda + boleh ada, boleh tidak.
SELECT nama, telepon
FROM kontak
WHERE telepon ~ '^\+?62'
ORDER BY id;

regex-invalid.sql

Unduh berkas
sql
-- Pola regex yang tidak valid ditolak server saat query dieksekusi:
-- kurung buka '(' tidak pernah ditutup, sehingga polanya tidak seimbang.
SELECT nama
FROM kontak
WHERE nama ~ '('
ORDER BY id;

similar-to.sql

Unduh berkas
sql
-- SIMILAR TO: pola gaya SQL standar (wildcard % dan _ ditambah regex sederhana).
SELECT nama
FROM kontak
WHERE nama SIMILAR TO '%(Budi|Siti)%'
ORDER BY id;

-- Operator ~ dengan regex penuh menghasilkan hasil yang sama...
SELECT nama
FROM kontak
WHERE nama ~ 'Budi|Siti'
ORDER BY id;

-- ...tetapi operator ~ jauh lebih kaya: kelas karakter, {n,m}, word boundary,
-- dan kompatibel dengan fungsi regexp_* — alasan regex POSIX menjadi pilihan utama.

word-boundary.sql

Unduh berkas
sql
CREATE TABLE pengumuman (
    id  integer PRIMARY KEY,
    isi text NOT NULL
);

INSERT INTO pengumuman (id, isi) VALUES
    (1, 'Transfer dana talangan toko sudah cair hari Senin.'),
    (2, 'Perdana menteri meresmikan gudang baru bulan depan.'),
    (3, 'Pengiriman tahap kedua dijadwalkan pekan ini.'),
    (4, 'Kedua paket tiba dua hari kemudian.');

-- Tanpa word boundary: pola 'dana' juga menempel di dalam kata 'Perdana'.
SELECT isi
FROM pengumuman
WHERE isi ~ 'dana'
ORDER BY id;

-- Dengan word boundary \y : pola harus berdiri sebagai kata utuh.
SELECT isi
FROM pengumuman
WHERE isi ~ '\ydana\y'
ORDER BY id;

-- 'dua' menempel di dalam 'kedua' — pola tanpa boundary cocok di baris 3 dan 4.
SELECT isi
FROM pengumuman
WHERE isi ~ 'dua'
ORDER BY id;

-- \ydua\y hanya mencocokkan 'dua' yang berdiri sendiri sebagai kata.
SELECT isi
FROM pengumuman
WHERE isi ~ '\ydua\y'
ORDER BY id;