Sắp xếp với ORDER BY
Bạn sẽ học được
- Sắp xếp kết quả bằng
ORDER BYtheo một hoặc nhiều cột. - Hiểu
ASClà mặc định và cách sắp xếp hoạt động với số, chuỗi, ngày. - Dùng bí danh cột trong
ORDER BY— điều không làm được trongWHERE. - Kiểm soát thứ tự của các giá trị bằng nhau bằng cột phụ.
- Nhận ra vì sao thứ tự kết quả có thể khác nhau giữa các lần chạy nếu thiếu
ORDER BY.
Khái niệm cốt lõi
ORDER BY sắp xếp các dòng sau khi đã lọc. Cú pháp:
SELECT name, price
FROM products
ORDER BY price;Không có ASC hay DESC, mặc định là tăng dần (ASC). Số nhỏ trước, số lớn sau. Chuỗi theo thứ tự của collation. Ngày cũ trước, ngày mới sau. Đây là thứ tự tự nhiên của từng kiểu dữ liệu.
Có thể sắp theo nhiều cột. Khi hai dòng bằng nhau ở cột đầu, cột thứ hai quyết định:
SELECT customer_id, order_date, id
FROM orders
ORDER BY customer_id, order_date DESC;Câu này nhóm đơn theo khách, trong mỗi khách đơn mới nhất đứng trước. Đây là dạng sắp xếp rất thường dùng trong báo cáo: nhóm chính tăng dần, chi tiết trong nhóm giảm dần.
Thứ tự sắp xếp so với thứ tự viết câu lệnh: ORDER BY chạy sau SELECT, nên bí danh cột dùng được ở đây, khác với WHERE:
SELECT name, price * 1.1 AS gia_sau_thue
FROM products
ORDER BY gia_sau_thue DESC;Câu này chạy đúng, dù gia_sau_thue chỉ được định nghĩa trong SELECT. Lý do: lúc ORDER BY thực thi, danh sách cột đã có bí danh. Với WHERE thì ngược lại — đây là điểm phân biệt quan trọng giữa hai mệnh đề.
Với chuỗi, thứ tự phụ thuộc collation — bộ quy tắc so sánh ký tự. PostgreSQL dùng collation của database hoặc của cột. Tiếng Việt có dấu khiến thứ tự không hoàn toàn giống alphabet tiếng Anh: Đ thường đứng sau D, ă gần a, v.v. Trong bộ dữ liệu nhỏ này, tên khách bắt đầu bằng các chữ cái Latin thông thường nên kết quả dễ đoán, nhưng khi làm báo cáo thật, hãy kiểm tra collation trước khi cam kết thứ tự.
ORDER BY không đảm bảo thứ tự giữa các dòng bằng nhau ở mọi cột được sắp. Nếu hai sản phẩm cùng giá và bạn chỉ sắp theo giá, PostgreSQL được phép trả chúng theo bất kỳ thứ tự nào, và thứ tự có thể đổi giữa các lần chạy hoặc sau khi dữ liệu thay đổi. Muốn kết quả ổn định, thêm cột phân biệt duy nhất:
ORDER BY price DESC, idCột id làm tie-breaker. Quy tắc này đặc biệt quan trọng khi dùng LIMIT, vì nếu các dòng ở biên bằng nhau, mỗi lần chạy có thể lấy một tập khác nhau.
Một lưu ý về hiệu năng: ORDER BY không có chỉ mục phù hợp sẽ phải sắp xếp toàn bộ kết quả trong bộ nhớ. Với vài chục nghìn dòng, không đáng lo. Với hàng triệu dòng, chỉ mục trên cột sắp xếp giúp tránh bước này. Đây là lý do các truy vấn "top N" trên bảng lớn thường đi kèm chỉ mục (cot_sap_xep DESC).
Ví dụ chạy được
Sắp sản phẩm theo giá tăng dần:
SELECT name, price
FROM products
ORDER BY price;Chín dòng, từ rẻ đến đắt:
| name | price |
|---|---|
| Sách SQL thực chiến | 180000 |
| Áo thun cotton | 250000 |
| Đèn bàn LED | 290000 |
| Bình giữ nhiệt | 320000 |
| Giá kê laptop | 390000 |
| Chuột không dây | 450000 |
| Tai nghe Bluetooth | 890000 |
| Bàn phím cơ | 1250000 |
| Nồi chiên không dầu | 1690000 |
Sắp theo tên khách:
SELECT name, city
FROM customers
ORDER BY name;Mười hai dòng, bắt đầu An Nguyễn, Bình Trần, Chi Lê, Dũng Phạm, Em Võ, Giang Đặng, Hà Bùi, Inh Đỗ, Khánh Hồ, Linh Ngô, Minh Dương, Nga Lý. Thứ tự này trùng với alphabet vì chữ cái đầu khác nhau. Nếu hai khách cùng tên, cần thêm cột phụ để ổn định.
Sắp nhiều cột trên bảng đơn:
SELECT id, customer_id, order_date
FROM orders
WHERE customer_id IN (1, 2, 3)
ORDER BY customer_id, order_date;Kết quả nhóm theo khách 1, 2, 3; trong mỗi nhóm ngày tăng dần. Khách 1 có đơn 1 (05/01) rồi 4 (20/02); khách 2 có đơn 2 (18/01), 6 (15/03), 12 (04/07); khách 3 có đơn 3 (02/02) và 16 (11/11). Cách đọc này giúp thấy ngay mỗi khách mua vào những mốc nào.
Dùng bí danh trong ORDER BY:
SELECT name, price * 1.1 AS gia_sau_thue
FROM products
ORDER BY gia_sau_thue DESC
LIMIT 3;Ba dòng đầu là Nồi chiên, Bàn phím, Tai nghe — vẫn theo thứ tự giá gốc vì phép nhân 1.1 giữ nguyên thứ hạng. Bí danh hoạt động; thử đưa gia_sau_thue vào WHERE sẽ thấy lỗi ngay.
Một ví dụ cho thấy ORDER BY không tự tạo thứ tự ổn định khi có giá trị bằng nhau. Bộ dữ liệu hiện tại không có hai sản phẩm cùng giá, nên hãy tưởng tượng thêm một sản phẩm giá 450.000. ORDER BY price DESC LIMIT 5 có thể trả sản phẩm này hoặc sản phẩm Chuột không dây ở vị trí thứ tư, tùy kế hoạch thực thi. Thêm , id vào ORDER BY là cách duy nhất để kết quả ổn định.
Thực hành
- Chạy
SELECT name, price FROM products ORDER BY price;rồi đảo thànhORDER BY price DESC. So sánh hai danh sách; tổng số dòng phải vẫn là 9. - Sắp khách theo
signup_datetăng dần. Người đăng ký sớm nhất là An Nguyễn (12/03/2023), muộn nhất là Nga Lý (30/06/2025). - Viết truy vấn sắp đơn theo
statustăng dần, trong mỗi trạng thái sắp theoorder_dategiảm dần. Quan sát ba nhóm trạng thái. - Thử dùng bí danh
SELECT price AS gia FROM products ORDER BY gia;và xác nhận nó chạy. Sau đó thử cùng bí danh trongWHEREđể thấy lỗi — ghi lại thông báo.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Kết quả không theo thứ tự mong đợi | Viết ORDER BY sau LIMIT | ORDER BY đứng trước LIMIT |
column "gia" does not exist | Dùng bí danh trong WHERE | Lặp biểu thức trong WHERE |
| Thứ tự đổi giữa các lần chạy | Các dòng bằng nhau ở cột sắp xếp | Thêm cột duy nhất làm tie-breaker |
Số bị sắp như chuỗi (100 trước 20) | Cột lưu kiểu text | Ép kiểu hoặc sửa schema |
| Dấu tiếng Việt sắp xếp lạ | Collation của database | Kiểm tra collation, dùng cột chuẩn hóa |
Lỗi "số bị sắp như chuỗi" rất hay gặp khi dữ liệu được nhập từ Excel hoặc CSV mà cột giá bị lưu dưới dạng text. Khi đó '1000000' đứng trước '200000' vì so sánh ký tự đầu. Cách sửa đúng là đổi kiểu cột, không phải thêm CAST rải rác trong mọi truy vấn.
Tóm tắt
ORDER BYchạy sauSELECT; mặc địnhASC, thêmDESCđể đảo.- Có thể sắp nhiều cột; cột sau quyết định khi cột trước bằng nhau.
- Bí danh
SELECTdùng được trongORDER BY, không dùng được trongWHERE. - Thêm cột duy nhất làm tie-breaker để kết quả ổn định, nhất là khi có
LIMIT. - Thứ tự chuỗi phụ thuộc collation; đừng giả định alphabet tiếng Anh.
Use ← → to move between lessons