Klausa LIMIT dan OFFSET yang kita pakai di chapter sebelumnya adalah sintaks khas PostgreSQL — kuat dan ringkas, tetapi bukan bagian standar SQL. Standar SQL:2008 mendefinisikan cara lain untuk kebutuhan yang sama: FETCH FIRST n ROWS ONLY dan OFFSET ... FETCH, yang juga didukung penuh oleh PostgreSQL. Selain soal kepatuhan standar, sintaks FETCH membawa satu kemampuan yang tidak dimiliki LIMIT sama sekali: opsi WITH TIES yang otomatis meloloskan baris-baris yang seri dengan baris terakhir potongan — misalnya saat tiga besar seleksi kandidat ternyata diisi empat orang karena dua kandidat mengantongi nilai yang sama persis. Chapter penutup section ini membahas FETCH dari bentuk dasarnya sampai WITH TIES.
Menyiapkan Tabel Latihan #
Tabel skor_tes menyimpan hasil tes delapan kandidat rekrutmen Toko Maju Jaya. Dua pasang nilai sengaja dibuat identik (dua kandidat ber-skor 85, dua ber-skor 78) — bahan baku utama demo WITH TIES:
CREATE TABLE skor_tes (
id integer PRIMARY KEY,
kandidat text NOT NULL,
skor integer NOT NULL
);
INSERT INTO skor_tes (id, kandidat, skor) VALUES
(1, 'Budi Santoso', 78),
(2, 'Siti Rahayu', 85),
(3, 'Andi Wijaya', 85),
(4, 'Dewi Lestari', 92),
(5, 'Rudi Hartono', 74),
(6, 'Maya Putri', 88),
(7, 'Agus Salim', 78),
(8, 'Rina Melati', 81);Output yang Dihasilkan:
CREATE TABLE
INSERT 0 8FETCH FIRST: Padanan Standar untuk LIMIT #
Posisi klausa FETCH sama dengan LIMIT: setelah ORDER BY, sebelum klausa penutup lain. Baca saja FETCH FIRST 3 ROWS ONLY sebagai kalimat: “ambil 3 baris pertama saja”:
-- FETCH FIRST n ROWS ONLY: padanan standar SQL:2008 dari LIMIT n.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 3 ROWS ONLY;
-- ROW dan ROWS maknanya identik; pilih yang selaras jumlahnya.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 1 ROW ONLY;
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 1 ROWS ONLY;
-- NEXT adalah sinonim FIRST; angka juga boleh dihilangkan (default 1).
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH NEXT ROW ONLY;Output yang Dihasilkan:
id | kandidat | skor
----+--------------+------
4 | Dewi Lestari | 92
6 | Maya Putri | 88
2 | Siti Rahayu | 85
(3 rows)
id | kandidat | skor
----+--------------+------
4 | Dewi Lestari | 92
(1 row)
id | kandidat | skor
----+--------------+------
4 | Dewi Lestari | 92
(1 row)
id | kandidat | skor
----+--------------+------
4 | Dewi Lestari | 92
(1 row)Query pertama mengembalikan tiga peringkat teratas — hasil yang persis sama dengan LIMIT 3 pada data dan ORDER BY yang sama. Tiga query berikutnya memperlihatkan keluwesan kosmetik sintaksnya: ROW dan ROWS sama-sama diterima untuk satu maupun banyak baris, NEXT adalah sinonim dari FIRST, dan bila angkanya dihilangkan, FETCH NEXT ROW ONLY berarti tepat satu baris. Satu perbedaan teknis kecil menanti di akhir chapter: nilai LIMIT boleh ekspresi telanjang seperti LIMIT 1+2, sedangkan FETCH meminta tanda kurung untuk hal yang sama.
Pagination dengan OFFSET dan FETCH #
Untuk pagination, FETCH berpasangan dengan OFFSET — dengan satu peningkatan kosmetik dibanding LIMIT: kata ROW/ROWS boleh ditempel setelah angka offset, membuat klausa terbaca seperti kalimat:
-- Pola pagination versi standar: OFFSET dulu, baru FETCH.
-- Ini halaman kedua (lompati 3 peringkat teratas, ambil 3 berikutnya).
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
OFFSET 3 ROWS
FETCH FIRST 3 ROWS ONLY;
-- LIMIT dan OFFSET menghasilkan hasil yang identik untuk kasus yang sama.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
LIMIT 3 OFFSET 3;
-- ROW dan ROWS juga bebas dipilih pada OFFSET.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
OFFSET 3 ROW
FETCH FIRST 2 ROWS ONLY;Output yang Dihasilkan:
id | kandidat | skor
----+--------------+------
3 | Andi Wijaya | 85
8 | Rina Melati | 81
1 | Budi Santoso | 78
(3 rows)
id | kandidat | skor
----+--------------+------
3 | Andi Wijaya | 85
8 | Rina Melati | 81
1 | Budi Santoso | 78
(3 rows)
id | kandidat | skor
----+-------------+------
3 | Andi Wijaya | 85
8 | Rina Melati | 81
(2 rows)Dua query pertama mengembalikan baris yang identik — peringkat ke-4 sampai ke-6 — membuktikan ekuivalensi OFFSET 3 ROWS FETCH FIRST 3 ROWS ONLY dengan LIMIT 3 OFFSET 3. Formula pagination dari chapter sebelumnya berlaku tanpa perubahan: OFFSET = (nomor_halaman - 1) × ukuran_halaman, dengan ORDER BY yang menentukan urutan penuh dipasang di setiap halaman.
WITH TIES: Meloloskan Baris yang Seri #
Inilah fitur yang membuat FETCH lebih dari sekadar ganti baju LIMIT. FETCH FIRST n ROWS ONLY memotong tepat maksimal n baris — bila baris ke-(n+1) nilainya sama persis dengan baris ke-n, baris itu terbuang. Opsi WITH TIES mengubah aturan potongnya: ambil n baris pertama, lalu ikutkan semua baris yang seri dengan baris terakhir yang diambil:
-- ROWS ONLY selalu memotong tepat maksimal n baris,
-- meski baris berikutnya seri dengan baris ke-n.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 3 ROWS ONLY;
-- WITH TIES pada kunci sort penuh (skor DESC, id): tidak ada seri yang
-- tersisa pada seluruh kunci sort, jadi hasilnya tetap 3 baris.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 3 ROWS WITH TIES;
-- WITH TIES pada kunci skor saja: dua kandidat seri di posisi ketiga
-- (Siti dan Andi, keduanya 85) sama-sama diloloskan — hasilnya 4 baris.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC
FETCH FIRST 3 ROWS WITH TIES;Output yang Dihasilkan:
id | kandidat | skor
----+--------------+------
4 | Dewi Lestari | 92
6 | Maya Putri | 88
2 | Siti Rahayu | 85
(3 rows)
id | kandidat | skor
----+--------------+------
4 | Dewi Lestari | 92
6 | Maya Putri | 88
2 | Siti Rahayu | 85
(3 rows)
id | kandidat | skor
----+--------------+------
4 | Dewi Lestari | 92
6 | Maya Putri | 88
2 | Siti Rahayu | 85
3 | Andi Wijaya | 85
(4 rows)Tiga hasil di atas memuat pelajaran penting tentang cara kerja WITH TIES:
- Query pertama (
ROWS ONLY) mengembalikan tiga baris:Dewi(92),Maya(88),Siti(85). Baris keempat —Andidengan skor 85 yang sama — terbuang meski secara nilai setara dengan peringkat ketiga. - Query kedua (
WITH TIES+ kunci sort penuh) tetap mengembalikan tiga baris.WITH TIESmenilai seri berdasarkan seluruh kunciORDER BY; karenaidikut menjadi kunci sort,Siti(id 2) danAndi(id 3) tidak lagi seri menurut server — tidak ada yang bisa diloloskan. - Query ketiga (
WITH TIES+ sort skor saja) mengembalikan empat baris. Kunci sortskormenyisakan seri asli di posisi ketiga, danWITH TIESmeloloskanAndibersamaSiti— “tiga besar” yang jujur terhadap nilai.
Kesimpulan praktisnya: pemutus seri seperti id (kebiasaan determinisme course ini) dan WITH TIES saling meniadakan efeknya. Gunakan ROWS ONLY plus pemutus seri saat Anda ingin potongan yang selalu sama jumlahnya; gunakan WITH TIES tanpa pemutus seri pada kolom utama saat makna bisnisnya menuntut keadilan nilai — peringkat seleksi, papan skor, batas kelulusan. Dua baris yang seri lepas dari kunci sort tidak punya urutan yang dijamin di antara mereka sendiri; bila tampilannya perlu konsisten, tambahkan kolom tampilan sekunder setelah kolom nilai.
WITH TIES Menuntut ORDER BY #
“Baris yang seri” hanya punya arti bila ada urutan yang menjadi acuan — tanpa ORDER BY, dua baris tidak bisa dinyatakan seri apa pun. Karena itu PostgreSQL menolak WITH TIES tanpa klausa ORDER BY:
-- WITH TIES menuntut klausa ORDER BY, karena "seri" hanya punya arti
-- relatif terhadap urutan hasil. Query ini tidak punya ORDER BY.
SELECT id, kandidat, skor
FROM skor_tes
FETCH FIRST 3 ROWS WITH TIES;Output yang Dihasilkan:
psql:with-ties-error.sql:5: ERROR: WITH TIES cannot be specified without ORDER BY clauseError WITH TIES cannot be specified without ORDER BY clause terdeteksi saat parse, jadi query ditolak sebelum menyentuh data. Perilaku ini konsisten dengan DISTINCT ON yang kita temui di chapter DISTINCT: klausa yang maknanya bergantung pada urutan wajib disertai ORDER BY.
LIMIT atau FETCH: Mana yang Dipilih #
Kedua sintaks hidup berdampingan di PostgreSQL dengan hasil yang setara untuk kasus yang sama. Tabel berikut merangkum pertimbangan praktisnya:
| Aspek | LIMIT / OFFSET | FETCH FIRST / OFFSET ... FETCH |
|---|---|---|
| Status | ekstensi khas PostgreSQL | standar SQL:2008 |
| Potongan tepat n baris | LIMIT n | FETCH FIRST n ROWS ONLY |
| Meloloskan baris seri | tidak tersedia | WITH TIES (PG 13+) |
| Nilai berupa ekspresi | langsung: LIMIT 1+2 | perlu tanda kurung: FETCH FIRST (1+2) ROWS ONLY |
| Ragam kosmetik | satu bentuk | FIRST/NEXT, ROW/ROWS, angka boleh dihilangkan |
Untuk kode aplikasi yang sehari-hari bekerja di PostgreSQL, LIMIT tetap pilihan yang umum dan tidak salah. Pilih FETCH bila portabilitas antar-database SQL jadi perhatian, atau — alasan paling kuat — bila Anda membutuhkan WITH TIES, satu-satunya cara satu-perintah untuk memotong hasil dengan jujur terhadap nilai yang seri.
Dengan FETCH, perjalanan kita di Section Filtering dan Sorting tuntas: Anda kini bisa menyaring baris dengan operator perbandingan, logika, IN, BETWEEN, LIKE, regex, dan IS NULL; meringkasnya dengan DISTINCT; lalu menyajikannya terurut dan terbatas. Kebiasaan berikutnya yang akan melipatgandakan kekuatan query Anda adalah memproses nilai per baris — memotong string, memformat tanggal, menghitung — lewat function bawaan PostgreSQL. Section Fungsi SQL, dimulai dari chapter Fungsi String, membuka materi itu.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
buat-tabel.sql
Unduh berkasCREATE TABLE skor_tes (
id integer PRIMARY KEY,
kandidat text NOT NULL,
skor integer NOT NULL
);
INSERT INTO skor_tes (id, kandidat, skor) VALUES
(1, 'Budi Santoso', 78),
(2, 'Siti Rahayu', 85),
(3, 'Andi Wijaya', 85),
(4, 'Dewi Lestari', 92),
(5, 'Rudi Hartono', 74),
(6, 'Maya Putri', 88),
(7, 'Agus Salim', 78),
(8, 'Rina Melati', 81);
fetch-dasar.sql
Unduh berkas-- FETCH FIRST n ROWS ONLY: padanan standar SQL:2008 dari LIMIT n.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 3 ROWS ONLY;
-- ROW dan ROWS maknanya identik; pilih yang selaras jumlahnya.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 1 ROW ONLY;
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 1 ROWS ONLY;
-- NEXT adalah sinonim FIRST; angka juga boleh dihilangkan (default 1).
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH NEXT ROW ONLY;
fetch-offset.sql
Unduh berkas-- Pola pagination versi standar: OFFSET dulu, baru FETCH.
-- Ini halaman kedua (lompati 3 peringkat teratas, ambil 3 berikutnya).
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
OFFSET 3 ROWS
FETCH FIRST 3 ROWS ONLY;
-- LIMIT dan OFFSET menghasilkan hasil yang identik untuk kasus yang sama.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
LIMIT 3 OFFSET 3;
-- ROW dan ROWS juga bebas dipilih pada OFFSET.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
OFFSET 3 ROW
FETCH FIRST 2 ROWS ONLY;
with-ties-error.sql
Unduh berkas-- WITH TIES menuntut klausa ORDER BY, karena "seri" hanya punya arti
-- relatif terhadap urutan hasil. Query ini tidak punya ORDER BY.
SELECT id, kandidat, skor
FROM skor_tes
FETCH FIRST 3 ROWS WITH TIES;
with-ties.sql
Unduh berkas-- ROWS ONLY selalu memotong tepat maksimal n baris,
-- meski baris berikutnya seri dengan baris ke-n.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 3 ROWS ONLY;
-- WITH TIES pada kunci sort penuh (skor DESC, id): tidak ada seri yang
-- tersisa pada seluruh kunci sort, jadi hasilnya tetap 3 baris.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC, id
FETCH FIRST 3 ROWS WITH TIES;
-- WITH TIES pada kunci skor saja: dua kandidat seri di posisi ketiga
-- (Siti dan Andi, keduanya 85) sama-sama diloloskan — hasilnya 4 baris.
SELECT id, kandidat, skor
FROM skor_tes
ORDER BY skor DESC
FETCH FIRST 3 ROWS WITH TIES;