Skip to content

LIMIT & OFFSET: Phân trang

Khi dữ liệu quá lớn (hàng triệu dòng), không ai tải hết về một lúc. Chúng ta chia nhỏ ra từng trang (pagination) để hiển thị.

Cú pháp (Syntax)

sql
SELECT columns
FROM table_name
LIMIT number_of_records -- Lấy bao nhiêu dòng
OFFSET start_position;   -- Bỏ qua bao nhiêu dòng đầu

Ví dụ thực tế (Real-world Example)

Bạn đang làm API phân trang cho danh sách User, mỗi trang 10 người.

Trang 1 (Lấy 10 người đầu tiên):

sql
SELECT id, username FROM users
ORDER BY id
LIMIT 10 OFFSET 0;

Trang 2 (Bỏ qua 10 người đầu, lấy 10 người tiếp theo):

sql
SELECT id, username FROM users
ORDER BY id
LIMIT 10 OFFSET 10;

Công thức tổng quát:OFFSET = (page_number - 1) * page_size


💡 HPN Pro Tip: Cái chết của OFFSET (Offset Doom)

Đây là điều phân biệt Junior và Senior.

Khi bạn query LIMIT 10 OFFSET 1000000 (Trang thứ 100,000), database KHÔNG nhảy thẳng tới dòng thứ 1 triệu. Nó thực sự phải đọc và đếm 1,000,010 dòng, sau đó vứt bỏ 1,000,000 dòng đầu và chỉ trả về 10 dòng cuối. Càng về trang sau, API càng chậm (O(N)).

Giải pháp Advanced: Keyset Pagination (Seeking) Thay vì dùng OFFSET, hãy dùng con trỏ (thường là id hoặc created_at).

Ví dụ: Lấy trang tiếp theo của trang có User ID cuối cùng là 50.

sql
-- "Hãy tìm cho tôi 10 người có ID lớn hơn 50"
SELECT id, username FROM users
WHERE id > 50
ORDER BY id ASC
LIMIT 10;

Cách này dùng được Index của id, tốc độ cực nhanh (O(logN)) bất chấp dữ liệu lớn cỡ nào.

⚠️ Common Mistake

Quên ORDER BY khi dùng LIMIT. Nếu không có ORDER BY, LIMIT sẽ trả về các dòng ngẫu nhiên. Khi người dùng F5 hoặc qua trang khác, dữ liệu có thể nhảy lung tung hoặc trùng lặp.

Luôn luôn ORDER BY một cột Unique (như ID) khi phân trang!