Query yang lolos penyaringan dan terurut rapi masih bisa memuat terlalu banyak baris untuk ditampilkan sekaligus. Halaman beranda toko tidak memuat seluruh katalog — cukup beberapa produk unggulan; hasil pencarian dibagi per halaman, bukan disembur sekaligus. Pasangan klausa LIMIT dan OFFSET adalah alat MySQL untuk memotong hasil query: LIMIT menentukan berapa baris yang diambil, OFFSET menentukan berapa baris yang dilewati lebih dulu. Pada chapter ini kita akan berlatih kedua klausa tersebut, membangun paging halaman satu-dua-tiga yang deterministik, menguji dua kasus tepi, lalu merangkai kombinasi lengkap WHERE-ORDER BY-LIMIT.

Menyiapkan Tabel Latihan #

Kita pakai tabel produk dengan data yang sama seperti chapter-chapter sebelumnya pada Section Filtering dan Sorting. Sepuluh baris produk cukup untuk memperlihatkan setiap perilaku LIMIT dan OFFSET tanpa membuat output membengkak:

sql
DROP DATABASE IF EXISTS belajar_filter;
CREATE DATABASE belajar_filter;
USE belajar_filter;

Output yang Dihasilkan:

text
Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
sql
CREATE TABLE produk (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(100) NOT NULL,
  kategori VARCHAR(50) NOT NULL,
  harga DECIMAL(10,2) NOT NULL,
  stok INT NOT NULL DEFAULT 0
);

INSERT INTO produk (nama, kategori, harga, stok) VALUES
  ('Buku Tulis Sidu 38', 'Alat Tulis', 4500.00, 120),
  ('Pensil 2B', 'Alat Tulis', 3000.00, 200),
  ('Penghapus Putih', 'Alat Tulis', 2500.00, 150),
  ('Pulpen Gel Hitam', 'Alat Tulis', 5000.00, 80),
  ('Spidol Permanen', 'Alat Tulis', 10000.00, 45),
  ('Kertas HVS A4', 'Kertas', 27500.00, 20),
  ('Kertas Origami', 'Kertas', 12000.00, 60),
  ('Amplop Coklat', 'Kertas', 800.00, 300),
  ('Stipo 12 Gram', 'Perekat', 6000.00, 0),
  ('Selotip Bening', 'Perekat', 8000.00, 55);

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

LIMIT n: Memotong Jumlah Baris #

Bentuk paling sederhana menuliskan satu angka setelah LIMIT: hasil query diambil maksimal sebanyak angka itu, dihitung dari baris pertama. Query klasik “tiga produk termahal” memadukan ORDER BY ... DESC dengan LIMIT 3:

sql
SELECT nama, harga
FROM produk
ORDER BY harga DESC
LIMIT 3;

Output yang Dihasilkan:

text
+-----------------+----------+
| nama            | harga    |
+-----------------+----------+
| Kertas HVS A4   | 27500.00 |
| Kertas Origami  | 12000.00 |
| Spidol Permanen | 10000.00 |
+-----------------+----------+
3 rows in set (0.00 sec)

Hanya tiga baris tampil: Kertas HVS A4 (27500), Kertas Origami (12000), dan Spidol Permanen (10000). Tanpa LIMIT, hasilnya sepuluh baris; dengan LIMIT 3, baris keempat ke bawah dibuang setelah pengurutan selesai. Nilai LIMIT berlaku setelah seluruh proses WHERE dan ORDER BY selesai — inilah urutan yang membuat pola “urutkan dulu, potong kemudian” bekerja seperti yang kita harapkan.

Kalau jumlah baris hasil lebih sedikit dari nilai LIMIT, MySQL cukup mengembalikan seluruh baris yang ada tanpa error. Query berikut meminta lima produk Perekat padahal kategori itu hanya memuat dua:

sql
SELECT nama, kategori
FROM produk
WHERE kategori = 'Perekat'
ORDER BY harga DESC
LIMIT 5;

Output yang Dihasilkan:

text
+----------------+----------+
| nama           | kategori |
+----------------+----------+
| Selotip Bening | Perekat  |
| Stipo 12 Gram  | Perekat  |
+----------------+----------+
2 rows in set (0.00 sec)

Hasilnya dua baris, bukan lima. LIMIT adalah batas atas, bukan target yang harus terpenuhi.

OFFSET: Melewati Baris Teratas #

OFFSET m memerintahkan MySQL melewati m baris pertama sebelum mulai mengambil. MySQL juga menyediakan bentuk ringkas LIMIT m, n — offset ditulis lebih dulu, jumlah baris kedua — yang maknanya sama dengan LIMIT n OFFSET m. Kita bandingkan kedua bentuk secara berdampingan:

sql
SELECT id, nama, harga
FROM produk
ORDER BY id
LIMIT 3 OFFSET 3;

SELECT id, nama, harga
FROM produk
ORDER BY id
LIMIT 3, 3;

Output yang Dihasilkan:

text
+----+------------------+----------+
| id | nama             | harga    |
+----+------------------+----------+
|  4 | Pulpen Gel Hitam |  5000.00 |
|  5 | Spidol Permanen  | 10000.00 |
|  6 | Kertas HVS A4    | 27500.00 |
+----+------------------+----------+
3 rows in set (0.00 sec)

+----+------------------+----------+
| id | nama             | harga    |
+----+------------------+----------+
|  4 | Pulpen Gel Hitam |  5000.00 |
|  5 | Spidol Permanen  | 10000.00 |
|  6 | Kertas HVS A4    | 27500.00 |
+----+------------------+----------+
3 rows in set (0.00 sec)

Kedua hasil identik: baris id 4, 5, dan 6. Bentuk pertama terbaca eksplisit — “lewati 3, ambil 3”; bentuk kedua lebih ringkas tetapi mudah salah baca karena dua angkanya berbalik posisi makna. Kebiasaan amannya: pakai bentuk LIMIT n OFFSET m saat belajar dan saat kejelasan penting, dan waspadai bentuk koma yang muncul di kode orang lain.

Paging: Halaman 1, 2, 3 #

Kombinasi LIMIT dan OFFSET adalah fondasi paging — membagi daftar panjang menjadi halaman-halaman berukuran tetap. Syarat pentingnya satu: ORDER BY eksplisit wajib ada, karena tanpa urutan yang stabil, “halaman 2” bisa menampilkan baris yang sama dengan halaman 1 atau melompati baris tertentu. Kita bagi daftar produk terurut id menjadi halaman berukuran tiga baris:

sql
SELECT id, nama, harga
FROM produk
ORDER BY id
LIMIT 3 OFFSET 0;

Output yang Dihasilkan:

text
+----+--------------------+---------+
| id | nama               | harga   |
+----+--------------------+---------+
|  1 | Buku Tulis Sidu 38 | 4500.00 |
|  2 | Pensil 2B          | 3000.00 |
|  3 | Penghapus Putih    | 2500.00 |
+----+--------------------+---------+
3 rows in set (0.00 sec)
sql
SELECT id, nama, harga
FROM produk
ORDER BY id
LIMIT 3 OFFSET 3;

Output yang Dihasilkan:

text
+----+------------------+----------+
| id | nama             | harga    |
+----+------------------+----------+
|  4 | Pulpen Gel Hitam |  5000.00 |
|  5 | Spidol Permanen  | 10000.00 |
|  6 | Kertas HVS A4    | 27500.00 |
+----+------------------+----------+
3 rows in set (0.00 sec)
sql
SELECT id, nama, harga
FROM produk
ORDER BY id
LIMIT 3 OFFSET 6;

Output yang Dihasilkan:

text
+----+----------------+----------+
| id | nama           | harga    |
+----+----------------+----------+
|  7 | Kertas Origami | 12000.00 |
|  8 | Amplop Coklat  |   800.00 |
|  9 | Stipo 12 Gram  |  6000.00 |
+----+----------------+----------+
3 rows in set (0.00 sec)

Halaman 1 menampilkan id 1-3, halaman 2 menampilkan id 4-6, dan halaman 3 menampilkan id 7-9. Satu produk tersisa — id 10 — menjadi halaman 4 dengan LIMIT 3 OFFSET 9, yang hanya memuat satu baris karena sisa datanya habis. Setiap halaman memakai query yang sama; satu-satunya nilai yang berubah adalah offset.

Posisi offset selalu bisa dihitung dengan pola tetap:

text
offset = (nomor halaman - 1) x ukuran halaman
HalamanUkuranRumusOffset
13(1-1) x 30
23(2-1) x 33
33(3-1) x 36
43(4-1) x 39

Aplikasi nyata menerapkan rumus ini saat membangun query dari nomor halaman yang diminta pengguna: halaman 2 berukuran 20 baris menghasilkan LIMIT 20 OFFSET 20, halaman 3 menghasilkan LIMIT 20 OFFSET 40, dan seterusnya.

Kasus Tepi: LIMIT 0 dan Offset Melebihi Jumlah Baris #

Dua nilai ekstrem layak diuji agar tidak mengejutkan. Pertama, LIMIT 0 meminta nol baris:

sql
SELECT id, nama
FROM produk
ORDER BY id
LIMIT 0;

Output yang Dihasilkan:

text
Empty set (0.00 sec)

Hasilnya himpunan kosong — Empty set — tanpa error. LIMIT 0 sesekali dipakai untuk menguji bentuk query (valid atau tidaknya sintaks) tanpa memuat data.

Kedua, offset yang melebihi jumlah baris tersedia. Sepuluh baris produk dilewati seluruhnya oleh OFFSET 30:

sql
SELECT id, nama
FROM produk
ORDER BY id
LIMIT 3 OFFSET 30;

Output yang Dihasilkan:

text
Empty set (0.00 sec)

Hasilnya kembali kosong. MySQL tidak memperingatkan offset yang terlalu jauh — kasus ini muncul di aplikasi ketika pengguna membuka halaman nomor 100 dari daftar yang hanya punya 4 halaman. Deteksi “halaman kosong karena lewat batas” atau “halaman terakhir berisi sisa baris” menjadi tanggung jawab aplikasi, bukan server.

Kombinasi Lengkap: WHERE, ORDER BY, LIMIT #

Tiga klausa bekerja berurutan sesuai posisinya pada statement: WHERE menyaring, ORDER BY mengurutkan hasil yang lolos, LIMIT memotong hasil yang terurut. Query berikut mengambil dua produk Alat Tulis termahal:

sql
SELECT nama, kategori, harga
FROM produk
WHERE kategori = 'Alat Tulis'
ORDER BY harga DESC
LIMIT 2;

Output yang Dihasilkan:

text
+------------------+------------+----------+
| nama             | kategori   | harga    |
+------------------+------------+----------+
| Spidol Permanen  | Alat Tulis | 10000.00 |
| Pulpen Gel Hitam | Alat Tulis |  5000.00 |
+------------------+------------+----------+
2 rows in set (0.00 sec)

Mari kita bedah urutan kerjanya:

  1. WHERE kategori = 'Alat Tulis' — dari sepuluh produk, hanya lima produk Alat Tulis yang menjadi kandidat.
  2. ORDER BY harga DESC — lima kandidat diurutkan dari termahal: Spidol Permanen, Pulpen Gel Hitam, Buku Tulis Sidu 38, Pensil 2B, Penghapus Putih.
  3. LIMIT 2 — hanya dua baris teratas yang dikirim sebagai hasil akhir.

Menukar posisi klausa tidak mungkin — LIMIT ... WHERE ... langsung ditolak parser karena urutan baku statement adalah FROM, WHERE, ORDER BY, lalu LIMIT. Kesalahan umum lainnya terjadi saat LIMIT dipakai untuk “mengambil nilai terbesar” tanpa ORDER BY: hasilnya hanya baris pertama menurut urutan tak terjamin, bukan baris terbesar.

Membersihkan Database Latihan #

Latihan memotong hasil selesai. Kita jatuhkan database latihan:

sql
DROP DATABASE belajar_filter;

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)

Referensi #

Kita kini bisa mengendalikan tiga sisi hasil query sekaligus: isi (WHERE), urutan (ORDER BY), dan jumlah baris (LIMIT dan OFFSET). Satu kebutuhan tersisa pada section ini: hasil query kadang memuat baris kembar — kota pelanggan yang sama muncul berkali-kali, kombinasi kategori-pelanggan yang berulang. Chapter berikutnya membahas DISTINCT, kata kunci yang menyaring hasil menjadi kombinasi nilai yang unik.