Không có ORDER BY, thứ tự các hàng là không xác định
Một câu SELECT không có ORDER BY trả về các hàng theo bất kỳ thứ tự nào mà SQLite thấy tiện. Trên một bảng nhỏ, thứ tự đó thường trông giống thứ tự lúc chèn dữ liệu, khiến nhiều người tin vào nó. Đừng tin. Ngay khi một chỉ mục (index) được dùng, bảng lớn lên, hoặc kế hoạch truy vấn thay đổi, thứ tự đó có thể đổi mà không báo trước.
Nếu bạn quan tâm đến thứ tự các hàng, hãy nói rõ ra:
ORDER BY name mặc định sắp xếp tăng dần. Kết quả trả về là Ada, Boris, Chen, Rosa, theo thứ tự bảng chữ cái, lần nào cũng vậy, bất kể bảng được lưu trên đĩa ra sao.
ASC và DESC
ASC nghĩa là tăng dần (từ nhỏ đến lớn, từ A đến Z, từ cũ nhất đến mới nhất). DESC nghĩa là giảm dần, tức ngược lại. ASC là mặc định, nên bạn gần như luôn thấy nó bị bỏ qua:
Câu này trả về những người đăng ký gần đây nhất trước. Ngày được lưu dưới dạng chuỗi ISO 8601 (YYYY-MM-DD) sắp xếp đúng khi so sánh như văn bản. Đó là một trong những lý do định dạng này được ưa chuộng cho các cột ngày trong SQLite, vốn không có kiểu ngày riêng.
Sắp xếp theo nhiều cột
Khi cột sắp xếp đầu tiên có các giá trị trùng nhau, SQLite dùng cột thứ hai để phân định. Liệt kê các cột cách nhau bằng dấu phẩy, theo thứ tự ưu tiên:
Các hàng được nhóm theo quốc gia trước (FR trước US), và trong mỗi quốc gia thì sắp xếp theo tên. Mỗi cột có thể mang chiều sắp xếp riêng:
Quốc gia tăng dần, rồi trong mỗi quốc gia thì mới nhất đứng trước. ASC và DESC chỉ áp dụng cho cột đứng ngay cạnh chúng, không lan sang cột khác.
Sắp xếp theo biểu thức và bí danh
ORDER BY nhận bất kỳ biểu thức nào, không chỉ tên cột. Điều này hữu ích với các giá trị được tính toán:
Bí danh (alias) revenue trong danh sách SELECT hoàn toàn dùng được trong ORDER BY. Bạn cũng có thể viết lại biểu thức, ORDER BY price * quantity DESC, và kết quả vẫn như vậy.
Bạn cũng có thể sắp xếp theo vị trí cột, dù đây là thói quen nên tránh:
SELECT name, price FROM products ORDER BY 2 DESC;
2 nghĩa là cột thứ hai trong danh sách select. Cách này chạy được, nhưng nếu sau này ai đó đổi thứ tự các cột, ý nghĩa của việc sắp xếp sẽ âm thầm thay đổi. Hãy sắp xếp theo tên cột hoặc bí danh.
NULL nằm ở đâu
NULL nghĩa là "không biết", và SQLite phải quyết định các giá trị không biết thuộc về đâu khi sắp xếp. Quy tắc mặc định: NULL đứng đầu với ASC, đứng cuối với DESC.
Ada và Chen xuất hiện ở trên cùng, trước mọi ngày thật. Hiếm khi đó là điều bạn muốn khi cần "mới nhất trước". Thay đổi bằng NULLS LAST:
Giờ các ngày thật đứng trước, NULL nằm ở dưới cùng. NULLS FIRST làm điều ngược lại. Cả hai đều thuộc SQL chuẩn và chạy được trên SQLite 3.30 trở lên.
Sắp xếp không phân biệt hoa thường với COLLATE NOCASE
Mặc định SQLite so sánh văn bản theo kiểu nhị phân, tức sắp xếp theo mã Unicode. Điều đó có nghĩa là chữ hoa đứng trước chữ thường, nên 'Zoe' đứng trước 'apple':
Kết quả là Boris, Zoe, ada, apple: chữ hoa trước, rồi đến chữ thường. Để sắp xếp không phân biệt hoa thường, gắn collation NOCASE:
Giờ bạn nhận được ada, apple, Boris, Zoe. NOCASE chỉ coi các chữ ASCII từ A đến Z và từ a đến z là tương đương; nó không chuẩn hóa dấu hay các chữ cái ngoài ASCII. Để sắp xếp đúng cho nhiều ngôn ngữ, bạn cần một collation ở tầng ứng dụng, nhưng NOCASE vẫn ổn cho trường hợp tiếng Anh thông thường.
Thứ tự ngẫu nhiên
Đôi khi bạn muốn các hàng theo thứ tự ngẫu nhiên, ví dụ chọn một mục nổi bật mỗi ngày hoặc lấy mẫu vài hàng để kiểm thử. Hàm random() của SQLite trả về một số nguyên ngẫu nhiên; hãy sắp xếp theo nó:
Mỗi hàng nhận một giá trị ngẫu nhiên mới, và phép sắp xếp xáo trộn chúng. Cách này ổn với bảng nhỏ. Trên bảng lớn, ORDER BY random() chạy chậm vì phải tính một giá trị ngẫu nhiên cho mọi hàng rồi sắp xếp toàn bộ kết quả. Để lấy mẫu một hàng từ một bảng khổng lồ, các cách tinh vi hơn (ví dụ chọn một rowid ngẫu nhiên) sẽ nhanh hơn.
Những lỗi thường gặp
Vài điều hay khiến người ta vấp:
- Quên
ORDER BYmà vẫn mặc định có thứ tự. Không có nó, thứ tự là không xác định. Dù trông có vẻ ổn định, thật ra không phải. - Sắp xếp số được lưu dưới dạng văn bản. Theo thứ tự từ điển,
'10'đứng trước'2'. Nếu một cột cần sắp xếp theo số, hãy lưu nó với affinity kiểu số (hoặc ép kiểu:ORDER BY CAST(value AS INTEGER)). - Trộn ASC và DESC giữa các cột. Mỗi cột có chiều riêng.
ORDER BY a, b DESCsắp xếpatăng dần vàbgiảm dần, chứ không phải cả hai đều giảm dần. - Sắp xếp một tập kết quả khổng lồ chỉ để lấy vài hàng đầu. Kết hợp
ORDER BYvớiLIMITvà đặt chỉ mục trên cột sắp xếp. Đó là nội dung của trang tiếp theo.
Tiếp theo: LIMIT và OFFSET
Sắp xếp cho SQLite biết cách xếp các hàng; LIMIT và OFFSET cho nó biết trả về bao nhiêu hàng và bắt đầu từ đâu. Kết hợp lại, chúng là nền tảng của phân trang và các truy vấn "top N", sẽ được giới thiệu ở trang tiếp theo.
Câu hỏi thường gặp
Làm sao sắp xếp kết quả trong SQLite?
Thêm mệnh đề ORDER BY vào cuối câu SELECT và ghi tên cột cần sắp xếp. SELECT * FROM users ORDER BY name; sắp xếp tăng dần. Thêm DESC để sắp xếp giảm dần: ORDER BY name DESC. Nếu không có ORDER BY, thứ tự các hàng không được đảm bảo. Dù trông có vẻ ổn định, đừng bao giờ dựa vào nó.
Làm sao sắp xếp theo nhiều cột trong SQLite?
Liệt kê các cột, cách nhau bằng dấu phẩy: ORDER BY country, name. SQLite sắp xếp theo cột đầu tiên, rồi dùng cột thứ hai để phân định các hàng trùng giá trị. Mỗi cột có thể có chiều sắp xếp riêng: ORDER BY country ASC, signup_date DESC.
Làm sao sắp xếp không phân biệt chữ hoa chữ thường trong SQLite?
Dùng COLLATE NOCASE trong ORDER BY: ORDER BY name COLLATE NOCASE. Mặc định SQLite so sánh văn bản theo collation nhị phân, nên Zoe đứng trước apple. NOCASE coi chữ hoa và chữ thường là như nhau khi sắp xếp.
NULL nằm ở đâu trong kết quả đã sắp xếp của SQLite?
Mặc định, NULL đứng đầu khi sắp xếp tăng dần và đứng cuối khi sắp xếp giảm dần. Bạn có thể thay đổi điều này bằng NULLS FIRST hoặc NULLS LAST: ORDER BY signup_date DESC NULLS LAST giữ các ngày thật ở trên cùng và đẩy các giá trị thiếu xuống dưới.