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

Sắp xếp giảm dần

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

  • Đảo chiều sắp xếp bằng DESC và kết hợp nhiều chiều trong một câu.
  • Dùng DESC cho số, ngày và chuỗi với kết quả khác nhau.
  • Kiểm soát vị trí của NULL bằng NULLS FIRST / NULLS LAST.
  • Viết mẫu truy vấn "top N" đúng thứ tự.
  • Nhận ra lỗi khi sắp xếp hỗn hợp mà không kiểm tra từng cột.

Khái niệm cốt lõi

ASC là mặc định. DESC đảo chiều: số lớn trước, ngày mới trước, chuỗi theo thứ tự đảo của collation. Cú pháp đặt ngay sau cột:

code
SELECT name, price
FROM products
ORDER BY price DESC;

Mỗi cột có thể có chiều riêng:

code
SELECT category, name, price
FROM products
ORDER BY category ASC, price DESC;

Đọc là: nhóm theo category tăng dần; trong mỗi nhóm, giá giảm dần. Đây là mẫu "nhóm chính — chi tiết phụ" rất phổ biến. Điều quan trọng là chiều được gắn vào từng cột, không phải vào cả câu. Viết ORDER BY category, price DESC cho kết quả giống hệt vì ASC là mặc định.

Với số, DESC cho danh sách từ lớn đến nhỏ — nền tảng của mọi báo cáo "top". Với ngày, DESC cho bản ghi mới nhất trước, phù hợp với bảng tin hoặc lịch sử giao dịch. Với chuỗi, DESC đảo thứ tự alphabet; ít dùng hơn nhưng hữu ích khi cần "Z đến A".

NULL có quy tắc mặc định đáng nhớ trong PostgreSQL:

ChiềuVị trí mặc định của NULL
ASCCuối cùng (NULLS LAST)
DESCĐầu tiên (NULLS FIRST)

Lý do: PostgreSQL coi NULL là lớn hơn mọi giá trị khác. Khi sắp tăng dần, nó nằm cuối; khi sắp giảm dần, nó nằm đầu. Nếu không muốn vậy, ghi rõ:

code
ORDER BY email DESC NULLS LAST

Điều này quan trọng trong báo cáo: một danh sách "khách mới nhất" sắp theo ngày đăng ký mà cột ngày cho phép NULL sẽ bắt đầu bằng các dòng thiếu ngày, không phải người mới nhất. Luôn kiểm tra dữ liệu thiếu trước khi cam kết thứ tự.

Một lưu ý về chuỗi: DESC không có nghĩa "từ A đến Z đảo ngược đẹp mắt" khi có dấu tiếng Việt. Collation quyết định, và đảo chiều chỉ đảo kết quả của collation đó. Với dữ liệu có nhiều ngôn ngữ, hãy thử trên dữ liệu thật trước khi dùng trong báo cáo.

Cuối cùng, mẫu top-N:

code
SELECT name, price
FROM products
ORDER BY price DESC
LIMIT 5;

Thứ tự thực thi là sắp xếp trước, cắt sau. Nếu đảo lại — LIMIT trước ORDER BY — câu lệnh sai cú pháp. Nếu bỏ ORDER BY, LIMIT 5 lấy năm dòng bất kỳ, không phải năm dòng đắt nhất. Đây là lỗi logic phổ biến nhất khi viết top-N.

Ví dụ chạy được

Sản phẩm từ đắt đến rẻ:

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

Chín dòng, đúng là đảo của danh sách tăng dần ở bài trước. Kiểm tra chéo này rất hữu ích: nếu hai danh sách không phải đảo của nhau, có thể bạn đã sắp sai cột hoặc dữ liệu thay đổi giữa hai lần chạy.

Khách mới đăng ký trước:

code
SELECT name, signup_date
FROM customers
ORDER BY signup_date DESC;

Mười hai dòng, bắt đầu Nga Lý (30/06/2025), Minh Dương (21/04/2025), Linh Ngô (09/03/2025), Khánh Hồ (14/02/2025), Inh Đỗ (10/01/2025), rồi Hà Bùi (02/11/2024), Giang Đặng (18/08/2024), Em Võ (05/05/2024), Dũng Phạm (20/02/2024), Chi Lê (15/01/2024), Bình Trần (01/07/2023), An Nguyễn (12/03/2023).

Sắp hai chiều trong một nhóm:

code
SELECT name, category, price
FROM products
WHERE category = 'Điện tử'
ORDER BY price DESC;

Ba dòng: Bàn phím cơ (1250000), Tai nghe Bluetooth (890000), Chuột không dây (450000). Lọc trước rồi sắp là cách đơn giản nhất để kiểm tra một nhóm mà không phụ thuộc thứ tự giữa các nhóm.

Sắp theo nhóm và giá:

code
SELECT category, name, price
FROM products
ORDER BY category, price DESC;

Kết quả gồm bốn nhóm. Trong nhóm Điện tử: Bàn phím, Tai nghe, Chuột. Trong nhóm Gia dụng: Nồi chiên, Giá kê, Bình giữ nhiệt, Đèn bàn. Nhóm Sách có một dòng, Thời trang có một dòng. Thứ tự giữa các nhóm phụ thuộc collation nên có thể khác giữa các môi trường; đừng viết báo cáo dựa vào thứ tự đó nếu chưa kiểm tra.

Minh họa NULL:

code
SELECT name, email
FROM customers
ORDER BY email DESC;

Hai dòng đầu là Dũng Phạm và Inh Đỗ — cả hai có email là NULL, và DESC đặt NULL lên trước. Nếu muốn họ ở cuối:

code
SELECT name, email
FROM customers
ORDER BY email DESC NULLS LAST;

Khi đó danh sách bắt đầu bằng nga@example.com và kết thúc bằng hai dòng NULL. Sự khác biệt này là lý do các báo cáo "mới nhất" cần chỉ định NULLS LAST nếu cột có thể thiếu.

Thực hành

  1. Viết truy vấn lấy ba sản phẩm đắt nhất. Kết quả phải là Nồi chiên, Bàn phím, Tai nghe. Sau đó đổi LIMIT 3 thành LIMIT 2 và dự đoán trước khi chạy.
  2. Sắp đơn theo order_date DESC và đếm năm đơn mới nhất. Đối chiếu với danh sách ngày trong bảng để chắc mình không bỏ sót.
  3. Viết truy vấn sắp khách theo segment DESC, name ASC. Giải thích vì sao thứ tự nhóm segment có thể khác dự đoán.
  4. Chạy truy vấn email DESC có và không có NULLS LAST. Đếm vị trí của hai dòng NULL để xác nhận quy tắc mặc định.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
Kết quả vẫn tăng dầnQuên DESCThêm DESC sau đúng cột
Chỉ cột đầu tiên bị đảoDESC đặt sau cột cuối nhưng không cho cột đầuGắn DESC riêng cho từng cột
Dòng NULL nằm đầu danh sáchMặc định của DESCThêm NULLS LAST
LIMIT lấy dòng bất kỳThiếu ORDER BYSắp xếp trước khi giới hạn
Hai lần chạy ra thứ tự khác nhauCác dòng bằng nhau ở cột sắp xếpThêm cột duy nhất làm tie-breaker

Lỗi "chỉ cột đầu tiên bị đảo" xảy ra khi viết ORDER BY category DESC, price và tưởng rằng DESC áp dụng cho cả hai. Thực tế price vẫn tăng dần. Muốn cả hai giảm, viết ORDER BY category DESC, price DESC.

Tóm tắt

  • DESC đảo chiều từng cột; ASC là mặc định.
  • Trong PostgreSQL, DESC đặt NULL lên đầu; dùng NULLS LAST nếu không muốn.
  • Top-N luôn là ORDER BY ... LIMIT n, không có ngoại lệ.
  • Kiểm tra chéo danh sách tăng dần và giảm dần để phát hiện lỗi.
  • Thứ tự nhóm chuỗi phụ thuộc collation; đừng giả định.

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