Xấu xí. Dữ liệu chưa được sắp xếp trông như thế nào. Chúng tôi làm cho dữ liệu dễ dàng nhìn thấy bằng cách sắp xếp chúng. Và đó là những gì SQL ORDER BY dành cho. Sử dụng một hoặc nhiều cột hoặc biểu thức làm cơ sở để sắp xếp dữ liệu. Sau đó, thêm ASC hoặc DESC để sắp xếp tăng dần hoặc giảm dần.
Cú pháp ORDER BY SQL:
ORDER BY <order_by_expression> [ASC | DESC]
Biểu thức ORDER BY có thể đơn giản như một danh sách các cột hoặc biểu thức. Nó cũng có thể có điều kiện bằng cách sử dụng khối CASE WHEN.
Nó rất linh hoạt.
Bạn cũng có thể sử dụng phân trang thông qua OFFSET và FETCH. Chỉ định số hàng cần bỏ qua và các hàng sẽ hiển thị.
Nhưng đây là tin xấu.
Thêm ORDER BY vào các truy vấn của bạn có thể làm chậm chúng. Và một số lưu ý khác có thể khiến ĐẶT HÀNG BẰNG CÁCH “không hoạt động”. Bạn không thể chỉ sử dụng chúng bất cứ lúc nào bạn muốn, vì có thể bị phạt. Vậy, chúng ta phải làm gì?
Trong bài viết này, chúng ta sẽ xem xét những điều nên làm và không nên khi sử dụng ORDER BY. Mỗi hạng mục sẽ giải quyết một vấn đề và giải pháp sẽ theo sau.
Sẵn sàng chưa?
Thực hiện trong SQL ORDER BY
1. Lập chỉ mục LỆNH SQL THEO (các) cột
Chỉ mục là tất cả về tìm kiếm nhanh chóng. Và có một trong các cột bạn sử dụng trong mệnh đề ORDER BY có thể tăng tốc truy vấn của bạn.
Hãy bắt đầu sử dụng ORDER BY trong cột không có chỉ mục. Chúng tôi sẽ sử dụng AdventureWorks cơ sở dữ liệu mẫu. Trước khi thực hiện truy vấn bên dưới, hãy tắt IX_SalesOrderDetail_ProductID chỉ mục trong SalesOrderDetail bàn. Sau đó, nhấn Ctrl-M và chạy nó.
-- Get order details by product and sort them by ProductID
USE AdventureWorks
GO
SET STATISTICS IO ON
GO
SELECT
ProductID
,OrderQty
,UnitPrice
,LineTotal
FROM Sales.SalesOrderDetail
ORDER BY ProductID
SET STATISTICS IO OFF
GO
PHÂN TÍCH
Đoạn mã trên sẽ xuất ra thống kê I / O trong tab Thông báo của SQL Server Management Studio. Bạn sẽ thấy kế hoạch thực hiện trong một tab khác.
KHÔNG CÓ CHỈ SỐ
Đầu tiên, hãy lấy số đọc logic từ IO THỐNG KÊ. Xem Hình 1.
Hình 1 . Đọc logic bằng ORDER BY của một cột không được lập chỉ mục. (Được định dạng bằng cách sử dụng Statisticsticsparser.com )
Không có chỉ mục, truy vấn đã sử dụng 1,313 lần đọc logic. Và đó là WorkTable ? Có nghĩa là SQL Server đã sử dụng TempDB để xử lý việc sắp xếp.
Nhưng điều gì đã xảy ra ở hậu trường? Hãy kiểm tra kế hoạch thực hiện trong Hình 2.
Hình 2 . Kế hoạch thực thi truy vấn sử dụng ORDER BY của một cột không được lập chỉ mục.
Bạn có thấy toán tử Parallelism (Gather Streams) không? Nó có nghĩa là SQL Server đã sử dụng nhiều hơn 1 bộ xử lý để xử lý truy vấn này. Truy vấn đủ nặng để yêu cầu nhiều CPU hơn.
Vì vậy, điều gì sẽ xảy ra nếu SQL Server sử dụng TempDB và nhiều bộ xử lý hơn? Thật tệ cho một truy vấn đơn giản.
VỚI MỘT CHỈ SỐ
Nó sẽ hoạt động như thế nào nếu chỉ mục được kích hoạt lại? Hãy cùng tìm hiểu. Tạo lại chỉ mục IX_SalesOrderDetail_ProductID . Sau đó, chạy lại truy vấn ở trên.
Kiểm tra các lần đọc logic mới trong Hình 3.
Hình 3 . Số lần đọc logic mới sau khi xây dựng lại chỉ mục.
Điều này tốt hơn nhiều. Chúng tôi đã cắt giảm gần một nửa số lần đọc logic. Điều đó có nghĩa là chỉ mục đã làm cho nó tiêu thụ ít tài nguyên hơn. Và WorkTable ? Nó đi rồi! Không cần sử dụng TempDB .
Và kế hoạch thực hiện? Xem Hình 4.
Hình 4 . Kế hoạch thực thi mới đơn giản hơn khi chỉ mục được tạo lại.
Xem? Kế hoạch đơn giản hơn. Không cần thêm CPU để sắp xếp 121.317 hàng giống nhau.
Vì vậy, điểm mấu chốt là: Đảm bảo các cột bạn sử dụng cho ORDER BY được lập chỉ mục .
NHƯNG ĐIỀU GÌ NẾU THÊM MỘT CHỈ SỐ ẢNH HƯỞNG ĐẾN HIỆU SUẤT VIẾT?
Câu hỏi hay.
Nếu đó là sự cố, bạn có thể kết xuất một phần của bảng nguồn vào một bảng tạm thời hoặc bảng được tối ưu hóa bộ nhớ . Sau đó, lập chỉ mục bảng đó. Sử dụng tương tự nếu có nhiều bảng hơn. Sau đó, đánh giá hiệu suất truy vấn của tùy chọn bạn đã chọn. Lựa chọn nhanh hơn sẽ là người chiến thắng.
2. Giới hạn kết quả với WHERE và OFFSET / FETCH
Hãy sử dụng một truy vấn khác. Giả sử bạn cần hiển thị thông tin sản phẩm bằng hình ảnh trong một ứng dụng. Hình ảnh có thể làm cho các truy vấn trở nên nặng nề hơn. Vì vậy, chúng tôi sẽ không chỉ kiểm tra các lần đọc logic mà còn kiểm tra các lần đọc logic.
Đây là mã.
SET STATISTICS IO ON
GO
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,b.Name AS ProductSubcategory
,d.ThumbNailPhoto
,d.LargePhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
ORDER BY ProductSubcategory, ProductName, a.Color
SET STATISTICS IO OFF
GO
Điều này sẽ xuất ra 97 xe đạp có hình ảnh. Chúng rất khó duyệt trên thiết bị di động.
PHÂN TÍCH
SỬ DỤNG ĐIỀU KIỆN TỐI THIỂU KHÔNG CÓ OFFSET / FETCH
Dưới đây là số lần đọc logic cần thiết để truy xuất 97 sản phẩm có hình ảnh. Xem Hình 5.
Hình 5 . Đọc logic và đọc logic khi sử dụng ORDER BY mà không có OFFSET / FETCH và với điều kiện WHERE tối thiểu . (Lưu ý :atisticsparser.com không hiển thị số lần đọc logic của lob. Ảnh chụp màn hình được chỉnh sửa dựa trên kết quả trong SSMS)
667 lần đọc logic lob xuất hiện vì lấy hình ảnh trong 2 cột. Trong khi đó, 590 lượt đọc logic đã được sử dụng cho phần còn lại.
Đây là kế hoạch thực thi trong Hình 6 để chúng ta có thể so sánh sau với kế hoạch tốt hơn.
Hình 6 . Kế hoạch thực thi sử dụng ORDER BY mà không có OFFSET / FETCH và với điều kiện WHERE tối thiểu.
Không có gì khác để nói cho đến khi chúng ta thấy kế hoạch thực hiện khác.
SỬ DỤNG ĐIỀU KIỆN BỔ SUNG Ở ĐÂU VÀ OFFSET / TÌM KIẾM ĐƠN HÀNG THEO
Bây giờ, hãy điều chỉnh truy vấn để đảm bảo dữ liệu tối thiểu được trả về. Đây là những gì chúng tôi sẽ làm:
- Thêm một điều kiện vào danh mục phụ sản phẩm. Trong ứng dụng gọi điện, chúng ta có thể hình dung việc cho phép người dùng chọn danh mục phụ.
- Sau đó, xóa danh mục phụ sản phẩm trong danh sách CHỌN cột và ĐẶT HÀNG THEO danh sách cột.
- Cuối cùng, thêm OFFSET / FETCH trong ORDER BY. Chỉ 10 sản phẩm sẽ được trả lại và hiển thị trong ứng dụng gọi điện.
Đây là mã đã chỉnh sửa.
DECLARE @pageNumber TINYINT = 1
DECLARE @noOfRows TINYINT = 10 -- each page will display 10 products at a time
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,d.ThumbNailPhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
AND a.ProductSubcategoryID = 2 -- Road Bikes
ORDER BY ProductName, a.Color
OFFSET (@pageNumber-1)*@noOfRows ROWS FETCH NEXT @noOfRows ROWS ONLY
Mã này sẽ cải thiện hơn nữa nếu bạn biến nó thành một quy trình được lưu trữ. Nó cũng sẽ có các tham số như số trang và số hàng. Số trang cho biết người dùng hiện đang xem trang nào. Cải thiện điều này hơn nữa bằng cách làm cho số lượng hàng linh hoạt tùy thuộc vào độ phân giải màn hình. Nhưng đó là một câu chuyện khác.
Bây giờ, chúng ta hãy xem xét các lần đọc logic trong Hình 7.
Hình 7 . Ít lần đọc logic hơn sau khi đơn giản hóa truy vấn. OFFSET / FETCH cũng được sử dụng trong ORDER BY.
Sau đó, so sánh Hình 7 với Hình 5. Các phép đọc logic của lob không còn nữa. Ngoài ra, số lần đọc logic có sự giảm đáng kể do tập kết quả cũng giảm từ 97 xuống 10.
Nhưng SQL Server đã làm gì đằng sau hậu trường? Kiểm tra kế hoạch thực hiện trong Hình 8.
Hình 8 . Một kế hoạch thực thi đơn giản hơn sau khi đơn giản hóa truy vấn và thêm OFFSET / FETCH trong ORDER BY.
Sau đó, so sánh Hình 8 với Hình 6. Không cần kiểm tra từng toán tử, chúng ta có thể thấy rằng phương án mới này đơn giản hơn phương án trước.
Bài học? Đơn giản hóa truy vấn của bạn. Sử dụng OFFSET / FETCH bất cứ khi nào có thể.
Những điều không nên trong SQL ORDER BY
Chúng tôi đã hoàn thành những việc cần làm khi sử dụng ORDER BY. Lần này, hãy tập trung vào những điều chúng ta nên tránh.
3. Không sử dụng ORDER BY khi sắp xếp theo Khóa chỉ mục được nhóm
Bởi vì nó vô dụng.
Hãy xem nó bằng một ví dụ.
SET STATISTICS IO ON
GO
-- Using ORDER BY with BusinessEntityID - the primary key
SELECT TOP 100 * FROM Person.Person
ORDER BY BusinessEntityID;
-- Without using ORDER BY at all
SELECT TOP 100 * FROM Person.Person;
SET STATISTICS IO OFF
GO
Sau đó, hãy kiểm tra các lần đọc logic của cả hai câu lệnh SELECT trong Hình 9.
Hình 9 . 2 truy vấn trên bảng Person hiển thị các lần đọc logic giống nhau. Một cái có ORDER BY, một cái khác không có.
Cả hai đều có 17 lần đọc logic. Điều này là hợp lý vì 100 hàng giống nhau được trả về. Nhưng liệu họ có cùng một kế hoạch? Xem Hình 10.
Hình 10 . Cùng một kế hoạch cho dù ORDER BY có được sử dụng hay không khi sắp xếp theo khóa chỉ mục nhóm.
Tuân thủ các toán tử giống nhau và chi phí truy vấn giống nhau.
Nhưng tại sao? Khi lập chỉ mục một hoặc nhiều cột thành một chỉ mục được nhóm lại, bảng sẽ được sắp xếp vật lý bằng khóa chỉ mục được phân cụm. Vì vậy, ngay cả khi bạn không sắp xếp theo khóa đó, kết quả vẫn sẽ được sắp xếp.
Điểm mấu chốt? Hãy tha thứ cho bản thân bằng cách không sử dụng khóa chỉ mục nhóm trong các trường hợp tương tự bằng cách sử dụng ORDER BY . Tiết kiệm năng lượng của bạn với ít lần nhấn phím hơn.
4. Không sử dụng ĐẶT HÀNG THEO Khi Cột Chuỗi Chứa Số
Nếu bạn sắp xếp theo cột chuỗi chứa số, đừng mong đợi thứ tự sắp xếp giống như các loại số thực. Nếu không, bạn sẽ có một bất ngờ lớn.
Đây là một ví dụ.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY NationalIDNumber;
Kiểm tra đầu ra trong Hình 11.
Hình 11 . Thứ tự sắp xếp của cột chuỗi có chứa số. Giá trị số không được theo sau.
Trong Hình 11, thứ tự sắp xếp từ vựng được tuân theo. Vì vậy, để khắc phục điều này, hãy sử dụng CAST thành một số nguyên.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT)
Xem Hình 12 để biết đầu ra cố định.
Hình 12 . CAST thành INT đã sửa lỗi sắp xếp cột chuỗi chứa số.
Vì vậy, tôi thay vì ORDER BY
5. Không sử dụng SELECT INTO #TempTable với ORDER BY
Thứ tự sắp xếp mong muốn của bạn sẽ không được đảm bảo trong bảng tạm thời mục tiêu. Xem tài liệu chính thức .
Hãy có một mã được sửa đổi từ ví dụ trước.
SELECT
NationalIDNumber
,JobTitle
,HireDate
INTO #temp
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT * FROM #temp;
Sự khác biệt duy nhất so với ví dụ trước là mệnh đề INTO. Đầu ra sẽ giống như trong Hình 11. Chúng ta quay lại hình vuông 1 ngay cả khi chúng ta ĐÚC cột thành INT.
Bạn cần tạo một bảng tạm thời bằng cách sử dụng CREATE TABLE. Nhưng hãy thêm một cột nhận dạng bổ sung và biến nó thành khóa chính. Sau đó, CHÈN vào bảng tạm thời.
Đây là mã cố định.
CREATE TABLE #temp2
(
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
NationalIDNumber NVARCHAR(15) NOT NULL,
JobTitle NVARCHAR(50) NOT NULL,
HireDate DATE NOT NULL
)
GO
INSERT INTO #temp2
(NationalIDNumber, JobTitle, HireDate)
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM #Temp2;
Và đầu ra sẽ giống như trong Hình 12. Nó hoạt động!
Những điểm rút ra khi sử dụng SQL ORDER BY
Chúng tôi đã đề cập đến các cạm bẫy phổ biến khi sử dụng SQL ORDER BY. Đây là bản tóm tắt:
Nên làm :
- Lập chỉ mục các cột ORDER BY,
- Giới hạn kết quả bằng WHERE và OFFSET / FETCH,
Không nên :
- Không sử dụng ORDER BY khi sắp xếp theo khóa chỉ mục nhóm,
- Không sử dụng ORDER BY khi cột chuỗi chứa số. Thay vào đó, hãy ĐÚC cột chuỗi thành INT trước.
- Không sử dụng SELECT INTO #TempTable với ORDER BY. Thay vào đó, hãy tạo bảng tạm thời trước với một cột nhận dạng bổ sung.
Mẹo và thủ thuật của bạn khi sử dụng ORDER BY? Cho chúng tôi biết trong phần ý kiến dưới đây. Và nếu bạn thích bài đăng này, hãy chia sẻ nó trên các nền tảng mạng xã hội yêu thích của bạn.