Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Triển khai một loại tùy chỉnh

Tôi thấy dòng tweet này được đưa ra…

Và nó khiến tôi nhìn vào những gì nó đang đề cập đến, bởi vì tôi đã không viết bất cứ điều gì 'gần đây' trên StackOverflow về dữ liệu đặt hàng. Hóa ra đó là câu trả lời này tôi đã viết , mặc dù không phải là câu trả lời được chấp nhận, nhưng đã thu về hơn một trăm phiếu bầu.

Người đặt câu hỏi có một vấn đề rất đơn giản - muốn có một số hàng nhất định xuất hiện đầu tiên. Và giải pháp của tôi rất đơn giản:

 ĐẶT HÀNG THEO TRƯỜNG HỢP KHI city ='New York' SAU ĐÓ 1 LẦN 2 KẾT THÚC, Thành phố; 

Đó dường như là một câu trả lời phổ biến, bao gồm cả Joel Sacco (theo tweet đó ở trên).

Ý tưởng là tạo thành một biểu thức và sắp xếp theo thứ tự đó. ORDER BY không quan tâm đó là một cột thực tế hay không. Bạn cũng có thể làm như vậy bằng cách sử dụng ÁP DỤNG, nếu bạn thực sự thích sử dụng một 'cột' trong mệnh đề ORDER BY của mình.

 CHỌN Người dùng. * TỪ UsersCROSS ÁP DỤNG (CHỌN TRƯỜNG HỢP KHI City ='New York' SAU ĐÓ 1 LẦN 2 KẾT THÚC NHƯ Or OrderCol) THEO o.OrorderCol, City; 

Nếu tôi sử dụng một số truy vấn chống lại WideWorldImporters, tôi có thể cho bạn thấy lý do tại sao hai truy vấn này thực sự giống hệt nhau. Tôi sẽ truy vấn bảng Sales.Orders, yêu cầu các Đơn đặt hàng cho Nhân viên bán hàng 7 xuất hiện trước. Tôi cũng sẽ tạo một chỉ mục bao quát thích hợp:

 TẠO CHỈ SỐ rf_Orders_SalesPeople_OrderDate ON Sales.Order (Sales Nhân viênPersonID) BAO GỒM (OrderDate); 

Các kế hoạch cho hai truy vấn này trông giống hệt nhau. Chúng thực hiện giống nhau - đọc giống nhau, biểu thức giống nhau, chúng thực sự là cùng một truy vấn. Nếu có sự khác biệt nhỏ về CPU hoặc Thời lượng thực tế, thì đó là một sự may mắn vì các yếu tố khác.

 CHỌN OrderID, Sales NHÂNPersonID, OrderDateFROM Sales.OrdersORDER THEO TRƯỜNG HỢP KHI Người bán hàngPersonID =7 THÌ 1 LẦN 2 KẾT THÚC, Người bán hàngPersonID; ÁP DỤNG CHỌN OrderID, Người bán hàngPersonID, Ngày đặt hàng 

Tuy nhiên, đây không phải là truy vấn mà tôi thực sự sẽ sử dụng trong tình huống này. Không phải nếu hiệu suất là quan trọng đối với tôi. (Thường là vậy, nhưng không phải lúc nào cũng đáng để viết một truy vấn nếu số lượng dữ liệu nhỏ.)

Điều làm tôi khó chịu là toán tử Sắp xếp đó. Đó là 96,4% chi phí!

Hãy xem xét nếu chúng tôi chỉ muốn đặt hàng qua Sales Nhân viên:

Chúng tôi thấy rằng chi phí CPU ước tính của truy vấn đơn giản hơn này là 1,4% của lô, trong khi của phiên bản được sắp xếp tùy chỉnh là 98,6%. Điều đó tồi tệ hơn SEVENTY TIMES. Mặc dù vậy, các bài đọc đều giống nhau - điều đó thật tốt. Thời lượng còn tệ hơn và CPU cũng vậy.

Tôi không thích thể loại. Chúng có thể khó chịu.

Một tùy chọn mà tôi có ở đây là thêm một cột được tính toán vào bảng của tôi và lập chỉ mục đó, nhưng điều đó sẽ có tác động đến bất kỳ thứ gì tìm kiếm tất cả các cột trên bảng, chẳng hạn như ORM, Power BI hoặc bất kỳ thứ gì có CHỌN * . Vì vậy, điều đó không quá tuyệt vời (mặc dù nếu chúng ta có thể thêm các cột được tính toán ẩn, điều đó sẽ tạo ra một tùy chọn thực sự tốt ở đây).

Một lựa chọn khác, dài dòng hơn (một số người có thể gợi ý rằng phù hợp với tôi - và nếu bạn nghĩ rằng:Ôi! Đừng thô lỗ như vậy!), Và sử dụng nhiều lượt đọc hơn, là xem xét những gì chúng ta sẽ làm trong cuộc sống thực nếu chúng tôi cần làm điều này.

Nếu tôi có một đống 73.595 đơn đặt hàng, được sắp xếp theo thứ tự của Nhân viên bán hàng và trước tiên tôi cần gửi lại chúng với một Nhân viên bán hàng cụ thể, tôi sẽ không bỏ qua thứ tự mà họ đang có và chỉ cần sắp xếp tất cả, tôi sẽ bắt đầu bằng cách đi sâu vào và tìm những người không phải là Nhân viên bán hàng 7 - giữ chúng theo thứ tự mà họ đã có. Sau đó, tôi sẽ tìm những người không phải là những người không phải là Nhân viên bán hàng 7 - đặt chúng tiếp theo và một lần nữa giữ chúng theo thứ tự đã có trong.

Trong T-SQL, điều đó được thực hiện như sau:

 CHỌN OrderID, Sales NHÂNPersonID, OrderDateFROM (CHỌN OrderID, Người bán hàngPersonID, Ngày đặt hàng, 1 AS Or OrderCol TỪ Bộ phận Bán hàng. Đơn hàng WHERE Người bán hàngPersonID =7 ĐOÀN KẾT CHỌN OrderID, Người bán hàngPersonID, Ngày đặt hàng, 2 NHƯ Or OrderCol TỪ Bộ phận bán hàng. Đơn hàng ĐÂU Người bán hàngPersonID! =7) oORDER BY o.Or OrderCol, o.SaleswomanPersonID; 

Điều này nhận được hai bộ dữ liệu và nối chúng. Nhưng Trình tối ưu hóa Truy vấn có thể thấy rằng nó cần phải duy trì thứ tự Sales Nhân viên PersonID, sau khi hai tập hợp được nối với nhau, do đó, nó thực hiện một kiểu nối đặc biệt để duy trì thứ tự đó. Đó là một phép tham gia Hợp nhất (Concatenation) và kế hoạch trông giống như sau:

Bạn có thể thấy nó phức tạp hơn rất nhiều. Nhưng hy vọng bạn cũng sẽ nhận thấy rằng không có toán tử Sắp xếp. Kết hợp Hợp nhất (Concatenation) kéo dữ liệu từ mỗi nhánh và tạo ra một tập dữ liệu theo đúng thứ tự. Trong trường hợp này, trước tiên nó sẽ kéo tất cả 7.276 hàng cho Nhân viên bán hàng 7, sau đó kéo 66.319 hàng khác, vì đó là thứ tự bắt buộc. Trong mỗi tập hợp, dữ liệu theo thứ tự SalesutorPersonID, thứ tự này được duy trì khi dữ liệu chảy qua.

Tôi đã đề cập trước đó rằng nó sử dụng nhiều lượt đọc hơn, và nó có. Nếu tôi hiển thị đầu ra SET STATISTICS IO, so sánh hai truy vấn, tôi thấy điều này:

Bảng 'Bàn làm việc'. Quét đếm 0, đọc lôgic 0, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, ghi lôgic vật lý 0, đọc trước lôgic đọc 0.
Bảng 'Đơn hàng'. Quét đếm 1, đọc lôgic 157, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, vận chuyển vật lý đọc 0, đọc trước tiểu giải đọc 0.


Bảng 'Đơn hàng '. Quét đếm 3, đọc lôgic 163, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, vận động vật lý đọc 0, đọc trước hành động đọc 0.

Sử dụng phiên bản "Sắp xếp tùy chỉnh", chỉ một lần quét chỉ mục, sử dụng 157 lần đọc. Bằng cách sử dụng phương pháp "Liên hợp Tất cả", đó là ba lần quét - một lần cho Người bán hàngPersonID =7, một lần cho Người bán hàngPersonID <7 và một lần cho Người bán hàngPersonID> 7. Chúng ta có thể thấy hai lần quét cuối cùng bằng cách xem các thuộc tính của Chỉ mục tìm kiếm thứ hai:

Tuy nhiên, đối với tôi, lợi ích mang lại khi thiếu Bàn làm việc.

Xem xét chi phí CPU ước tính:

Nó không nhỏ bằng 1,4% của chúng tôi khi chúng tôi tránh hoàn toàn việc sắp xếp, nhưng nó vẫn là một cải tiến lớn so với phương pháp Sắp xếp tùy chỉnh của chúng tôi.

Nhưng một lời cảnh báo…

Giả sử tôi đã tạo chỉ mục đó theo cách khác và có Ngày thứ tự làm cột chính thay vì cột được bao gồm.

 TẠO CHỈ SỐ rf_Orders_SalesPeople_OrderDate TRÊN Sales.Orders (Người bán hàngPersonID, OrderDate); 

Bây giờ, phương pháp "Liên hợp Tất cả" của tôi hoàn toàn không hoạt động như dự kiến.

Mặc dù sử dụng chính xác các truy vấn như trước đây, nhưng kế hoạch tốt đẹp của tôi hiện có hai toán tử Sắp xếp và nó hoạt động gần như kém so với phiên bản Quét + Sắp xếp ban đầu của tôi.

Lý do cho điều này là một sai sót của toán tử Kết hợp Hợp nhất (Kết hợp) và manh mối nằm trong toán tử Sắp xếp.

Nó được sắp xếp theo thứ tự của Người bán hàngPersonID theo sau là OrderID - là khóa chỉ mục nhóm của bảng. Nó chọn điều này bởi vì đây được biết là duy nhất và nó là một tập hợp các cột nhỏ hơn để sắp xếp so với SalesutorPersonID, theo sau là OrderDate, sau đó là OrderID, là thứ tự tập dữ liệu được tạo ra bởi ba lần quét phạm vi chỉ mục. Một trong những thời điểm mà Trình tối ưu hóa Truy vấn không nhận thấy một tùy chọn tốt hơn ở ngay đó.

Với chỉ mục này, chúng tôi cũng cần tập dữ liệu được OrderDate sắp xếp để tạo kế hoạch ưa thích của chúng tôi.

 CHỌN OrderID, Sales NHÂNPersonID, OrderDateFROM (CHỌN OrderID, Người bán hàngPersonID, Ngày đặt hàng, 1 AS Or OrderCol TỪ Bộ phận Bán hàng. Đơn hàng WHERE Người bán hàngPersonID =7 ĐOÀN KẾT CHỌN OrderID, Người bán hàngPersonID, Ngày đặt hàng, 2 NHƯ Or OrderCol TỪ Bộ phận bán hàng. Đơn hàng ĐÂU Người bán hàngPersonID! =7) oORDER BY o.Or OrderCol, o.SaleswomanPersonID, OrderDate; 

Vì vậy, chắc chắn là phải nỗ lực nhiều hơn. Tôi viết truy vấn lâu hơn, đọc nhiều hơn và tôi phải có một chỉ mục mà không có cột chính phụ. Nhưng nó chắc chắn nhanh hơn. Với nhiều hàng hơn nữa, tác động vẫn còn lớn hơn và tôi cũng không phải mạo hiểm khi Sắp xếp tràn sang tempdb.

Đối với các bộ nhỏ, câu trả lời StackOverflow của tôi vẫn tốt. Nhưng khi toán tử Sắp xếp đó làm tôi tốn hiệu suất, thì tôi sẽ sử dụng phương pháp Liên kết Tất cả / Hợp nhất (Kết hợp).


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách thay đổi văn bản thành chữ thường trong SQL

  2. Cơ sở dữ liệu là gì?

  3. Đặt thuộc tính kết nối ODBC mà không cần phải viết mã

  4. Cách tạo dạng xem trong SQL

  5. Sử dụng AT TIME ZONE để sửa một báo cáo cũ