DHDataHọc
Lý thuyếtBài 13/306 phút đọc+10 XP

Sắp xếp với ORDER BY

Bạn sẽ học được

  • Sắp xếp kết quả bằng ORDER BY theo một hoặc nhiều cột.
  • Hiểu ASC là 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 trong WHERE.
  • 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:

code
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:

code
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:

code
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:

code
ORDER BY price DESC, id

Cộ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:

code
SELECT name, price
FROM products
ORDER BY price;

Chín dòng, từ rẻ đến đắt:

nameprice
Sách SQL thực chiến180000
Áo thun cotton250000
Đèn bàn LED290000
Bình giữ nhiệt320000
Giá kê laptop390000
Chuột không dây450000
Tai nghe Bluetooth890000
Bàn phím cơ1250000
Nồi chiên không dầu1690000

Sắp theo tên khách:

code
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:

code
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:

code
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

  1. Chạy SELECT name, price FROM products ORDER BY price; rồi đảo thành ORDER BY price DESC. So sánh hai danh sách; tổng số dòng phải vẫn là 9.
  2. Sắp khách theo signup_date tă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).
  3. Viết truy vấn sắp đơn theo status tăng dần, trong mỗi trạng thái sắp theo order_date giảm dần. Quan sát ba nhóm trạng thái.
  4. 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 trong WHERE để thấy lỗi — ghi lại thông báo.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
Kết quả không theo thứ tự mong đợiViết ORDER BY sau LIMITORDER BY đứng trước LIMIT
column "gia" does not existDùng bí danh trong WHERELặp biểu thức trong WHERE
Thứ tự đổi giữa các lần chạyCác dòng bằng nhau ở cột sắp xếpThê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 databaseKiể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 BY chạy sau SELECT; mặc định ASC, thêm DESC để đả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 SELECT dùng được trong ORDER BY, không dùng được trong WHERE.
  • 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.

Dùng ← → để chuyển bài