Vấn đề
UNION mặc định loại bỏ duplicate và không bảo toàn thứ tự các SELECT con. Cần cách sắp xếp kết quả hợp nhất theo quy tắc tùy chỉnh.
Giải pháp: ORDER BY CASE thay vì UNION
Thay vì dùng UNION nhiều SELECT, dùng một SELECT duy nhất với ORDER BY CASE.
Ví dụ: Sắp xếp ngày giao hàng
Yêu cầu: Ngày tương lai trước (giảm dần), ngày quá khứ sau (giảm dần)
Tạo bảng và dữ liệu
mysql> CREATE TABLE DemoTable (
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
ShippingDate DATETIME
);
mysql> INSERT INTO DemoTable(ShippingDate) VALUES('2019-04-21');
mysql> INSERT INTO DemoTable(ShippingDate) VALUES('2019-01-01');
mysql> INSERT INTO DemoTable(ShippingDate) VALUES('2019-05-11');
mysql> INSERT INTO DemoTable(ShippingDate) VALUES('2018-12-31');
mysql> INSERT INTO DemoTable(ShippingDate) VALUES('2019-02-18');
Truy vấn với ORDER BY CASE
mysql> SELECT * FROM DemoTable
-> ORDER BY CASE
WHEN ShippingDate < CURDATE() THEN ShippingDate
ELSE CURDATE()
END DESC,
ShippingDate ASC;
Kết quả (giả sử CURDATE() = 2019-03-01)
+----+---------------------+ | Id | ShippingDate | +----+---------------------+ | 3 | 2019-05-11 00:00:00 | -- Tương lai: mới nhất trước | 1 | 2019-04-21 00:00:00 | | 5 | 2019-02-18 00:00:00 | -- Quá khứ: mới nhất trước | 2 | 2019-01-01 00:00:00 | | 4 | 2018-12-31 00:00:00 | +----+---------------------+
Giải thích logic CASE
ShippingDate < CURDATE(): Ngày quá khứ → trả về chính ngày đóELSE CURDATE(): Ngày tương lai → trả về hôm nay (giá trị cố định)ORDER BY ... DESC: Giá trị lớn hơn trước → CURDATE() (tương lai) lên đầuShippingDate ASC: Tie-breaker cho cùng nhóm
Mở rộng: Nhiều nhóm ưu tiên
ORDER BY CASE
WHEN status = 'urgent' THEN 1
WHEN status = 'high' THEN 2
WHEN status = 'normal' THEN 3
ELSE 4
END ASC,
created_at DESC;
Khi nào cần UNION?
- Kết hợp kết quả từ nhiều bảng khác cấu trúc
- Cần
UNION ALLđể giữ duplicate (nhanh hơn UNION) - Khi logic WHERE phức tạp khác nhau cho từng tập
Mẹo: Thường dùng ORDER BY FIELD(status, 'urgent','high','normal') cho đọc dễ hơn CASE.