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

Bạn được sắp xếp? Mẹo liên quan đến việc sắp xếp cửa sổ T-SQL

Một chỉ mục hỗ trợ có thể giúp tránh nhu cầu sắp xếp rõ ràng trong kế hoạch truy vấn khi tối ưu hóa các truy vấn T-SQL liên quan đến các hàm cửa sổ. Bởi một chỉ mục hỗ trợ, Ý tôi là một với các phần tử phân vùng cửa sổ và sắp xếp thứ tự làm khóa chỉ mục và phần còn lại của các cột xuất hiện trong truy vấn dưới dạng các cột bao gồm chỉ mục. Tôi thường gọi mẫu lập chỉ mục như vậy là POC chỉ mục dưới dạng từ viết tắt của phân vùng , đặt hàng, bao phủ . Đương nhiên, nếu một phần tử phân vùng hoặc sắp xếp không xuất hiện trong hàm cửa sổ, bạn bỏ qua phần đó khỏi định nghĩa chỉ mục.

Nhưng những truy vấn liên quan đến nhiều chức năng cửa sổ với các nhu cầu đặt hàng khác nhau thì sao? Tương tự, điều gì sẽ xảy ra nếu các phần tử khác trong truy vấn ngoài các hàm cửa sổ cũng yêu cầu sắp xếp dữ liệu đầu vào theo thứ tự trong kế hoạch, chẳng hạn như mệnh đề ORDER BY của bản trình bày? Điều này có thể dẫn đến các phần khác nhau của kế hoạch cần phải xử lý dữ liệu đầu vào theo các thứ tự khác nhau.

Trong những trường hợp như vậy, bạn thường sẽ chấp nhận việc sắp xếp rõ ràng là điều không thể tránh khỏi trong kế hoạch. Bạn có thể thấy sự sắp xếp theo cú pháp của các biểu thức trong truy vấn có thể ảnh hưởng đến bao nhiêu các toán tử sắp xếp rõ ràng bạn nhận được trong kế hoạch. Bằng cách làm theo một số mẹo cơ bản, đôi khi bạn có thể giảm số lượng toán tử sắp xếp rõ ràng, tất nhiên, điều này có thể có tác động lớn đến hiệu suất của truy vấn.

Môi trường cho bản trình diễn

Trong các ví dụ của mình, tôi sẽ sử dụng cơ sở dữ liệu mẫu PerformanceV5. Bạn có thể tải xuống mã nguồn để tạo và điền cơ sở dữ liệu này tại đây.

Tôi đã chạy tất cả các ví dụ trên SQL Server 2019 Developer, nơi chế độ hàng loạt trên cửa hàng có sẵn.

Trong bài viết này, tôi muốn tập trung vào các mẹo liên quan đến khả năng tính toán của hàm cửa sổ trong kế hoạch để dựa vào dữ liệu đầu vào có thứ tự mà không yêu cầu thêm hoạt động sắp xếp rõ ràng trong kế hoạch. Điều này có liên quan khi trình tối ưu hóa sử dụng xử lý chế độ hàng nối tiếp hoặc song song đối với các chức năng cửa sổ và khi sử dụng toán tử Tổng hợp cửa sổ chế độ hàng loạt nối tiếp.

SQL Server hiện không hỗ trợ kết hợp hiệu quả đầu vào bảo toàn thứ tự song song trước toán tử Tổng hợp cửa sổ chế độ lô song song. Vì vậy, để sử dụng toán tử Tổng hợp cửa sổ chế độ hàng loạt song song, trình tối ưu hóa phải đưa vào toán tử Sắp xếp chế độ hàng loạt song song trung gian, ngay cả khi đầu vào đã được sắp xếp trước.

Vì lợi ích của đơn giản, bạn có thể ngăn chặn song song trong tất cả các ví dụ được hiển thị trong bài viết này. Để đạt được điều này mà không cần thêm gợi ý cho tất cả các truy vấn và không cần đặt tùy chọn cấu hình trên toàn máy chủ, bạn có thể đặt tùy chọn cấu hình phạm vi cơ sở dữ liệu MAXDOP thành 1 , như vậy:

USE PerformanceV5;
 
ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 1;

Hãy nhớ đặt nó về 0 sau khi bạn kiểm tra xong các ví dụ trong bài viết này. Tôi sẽ nhắc bạn ở phần cuối.

Ngoài ra, bạn có thể ngăn song song ở cấp phiên với DBCC OPTIMIZER_WHATIF không có tài liệu lệnh, như vậy:

DBCC OPTIMIZER_WHATIF(CPUs, 1);

Để đặt lại tùy chọn khi bạn hoàn tất, hãy gọi lại tùy chọn đó với giá trị 0 là số CPU.

Khi bạn đã thử tất cả các ví dụ trong bài viết này đã tắt tính năng song song, tôi khuyên bạn nên bật chế độ song song và thử lại tất cả các ví dụ để xem những gì thay đổi.

Mẹo 1 và 2

Trước khi tôi bắt đầu với các mẹo, trước tiên hãy xem một ví dụ đơn giản với hàm cửa sổ được thiết kế để hưởng lợi từ chỉ mục supp class ="border indent shadow orting.

Hãy xem xét truy vấn sau đây, mà tôi sẽ gọi là Truy vấn 1:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1
 
FROM dbo.Orders;

Đừng lo lắng về thực tế là ví dụ được tạo ra. Không có lý do kinh doanh nào để tính toán tổng số ID đơn đặt hàng đang chạy — bảng này có kích thước phù hợp với các hàng 1MM và tôi muốn hiển thị một ví dụ đơn giản với hàm cửa sổ phổ biến, chẳng hạn như hàm áp dụng tính toán tổng đang chạy.

Sau lược đồ lập chỉ mục POC, bạn tạo chỉ mục sau để hỗ trợ truy vấn:

CREATE UNIQUE NONCLUSTERED INDEX idx_nc_cid_od_oid ON dbo.Orders(custid, orderdate, orderid);

Kế hoạch cho truy vấn này được thể hiện trong Hình 1.

Hình 1:Kế hoạch cho Truy vấn 1

Không có gì ngạc nhiên ở đây. Kế hoạch áp dụng quét thứ tự chỉ mục của chỉ mục bạn vừa tạo, cung cấp dữ liệu được sắp xếp theo thứ tự cho toán tử Tổng hợp cửa sổ, mà không cần phân loại rõ ràng.

Tiếp theo, hãy xem xét truy vấn sau, liên quan đến nhiều chức năng cửa sổ với các nhu cầu sắp xếp khác nhau, cũng như mệnh đề ORDER BY trong bản trình bày:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3
 
FROM dbo.Orders
ORDER BY custid, orderid;

Tôi sẽ gọi truy vấn này là Truy vấn 2. Kế hoạch cho truy vấn này được hiển thị trong Hình 2.

Hình 2:Kế hoạch cho Truy vấn 2

Lưu ý rằng có bốn toán tử Sắp xếp trong kế hoạch.

Nếu bạn phân tích các chức năng cửa sổ khác nhau và nhu cầu sắp xếp bản trình bày, bạn sẽ thấy có ba nhu cầu đặt hàng riêng biệt:

  • custid, orderdate, orderid
  • orderid
  • custid, orderid

Với một trong số chúng (cái đầu tiên trong danh sách ở trên) có thể được hỗ trợ bởi chỉ mục bạn đã tạo trước đó, bạn sẽ chỉ thấy hai loại trong kế hoạch. Vì vậy, tại sao kế hoạch có bốn loại? Có vẻ như SQL Server không cố gắng quá phức tạp trong việc sắp xếp lại thứ tự xử lý của các chức năng trong kế hoạch để giảm thiểu các loại. Nó xử lý các chức năng trong kế hoạch theo thứ tự chúng xuất hiện trong truy vấn. Đó ít nhất là trường hợp xảy ra lần đầu tiên đối với từng nhu cầu đặt hàng riêng biệt, nhưng tôi sẽ trình bày chi tiết về vấn đề này ngay sau đây.

Bạn có thể loại bỏ nhu cầu về một số loại trong kế hoạch bằng cách áp dụng hai phương pháp đơn giản sau:

Mẹo 1:Nếu bạn có chỉ mục để hỗ trợ một số chức năng cửa sổ trong truy vấn, hãy chỉ định những chức năng đó trước tiên.

Mẹo 2:Nếu truy vấn liên quan đến các chức năng cửa sổ có cùng nhu cầu sắp xếp như thứ tự bản trình bày trong truy vấn, hãy chỉ định các chức năng đó sau cùng.

Thực hiện theo các mẹo này, bạn sắp xếp lại thứ tự xuất hiện của các hàm cửa sổ trong truy vấn như sau:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
FROM dbo.Orders
ORDER BY custid, orderid;

Tôi sẽ gọi truy vấn này là Truy vấn 3. Kế hoạch cho truy vấn này được hiển thị trong Hình 3.

Hình 3:Kế hoạch cho Truy vấn 3

Như bạn có thể thấy, kế hoạch hiện chỉ có hai loại.

Mẹo 3

SQL Server không cố gắng quá phức tạp trong việc sắp xếp lại thứ tự xử lý của các chức năng cửa sổ nhằm giảm thiểu các loại trong kế hoạch. Tuy nhiên, nó có khả năng sắp xếp lại đơn giản nhất định. Nó quét các chức năng cửa sổ dựa trên thứ tự xuất hiện trong truy vấn và mỗi khi phát hiện nhu cầu thứ tự riêng biệt mới, nó sẽ tìm kiếm các chức năng cửa sổ bổ sung có cùng nhu cầu sắp xếp và nếu tìm thấy những thứ đó, nó sẽ nhóm chúng lại với nhau với lần xuất hiện đầu tiên. Trong một số trường hợp, nó thậm chí có thể sử dụng cùng một toán tử để tính toán nhiều hàm cửa sổ.

Hãy xem xét truy vấn sau đây làm ví dụ:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
  MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
  AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2
 
FROM dbo.Orders
ORDER BY custid, orderid;

Tôi sẽ gọi truy vấn này là Truy vấn 4. Kế hoạch cho truy vấn này được hiển thị trong Hình 4.

Hình 4:Kế hoạch cho Truy vấn 4

Các chức năng cửa sổ có cùng nhu cầu sắp xếp không được nhóm lại với nhau trong truy vấn. Tuy nhiên, vẫn chỉ có hai loại trong kế hoạch. Điều này là do những gì quan trọng về thứ tự xử lý trong kế hoạch là sự xuất hiện đầu tiên của mỗi nhu cầu đặt hàng riêng biệt. Điều này dẫn tôi đến mẹo thứ ba.

Mẹo 3:Đảm bảo thực hiện theo mẹo 1 và 2 cho lần xuất hiện đầu tiên của từng nhu cầu đặt hàng riêng biệt. Các lần xuất hiện tiếp theo của cùng một nhu cầu đặt hàng, ngay cả khi không liền kề, được xác định và nhóm lại với nhu cầu đầu tiên.

Mẹo 4 và 5

Giả sử bạn muốn trả về các cột kết quả từ các phép tính trong cửa sổ theo một thứ tự từ trái sang phải nhất định trong đầu ra. Nhưng điều gì sẽ xảy ra nếu thứ tự không giống với thứ tự sẽ giảm thiểu các loại trong kế hoạch?

Ví dụ:giả sử bạn muốn kết quả giống như kết quả do Truy vấn 2 tạo ra theo thứ tự cột từ trái sang phải trong đầu ra (thứ tự cột:cols khác, sum2, sum1, sum3), nhưng bạn muốn có cùng một kế hoạch giống như kế hoạch bạn nhận được cho Truy vấn 3 (thứ tự cột:cols khác, sum1, sum3, sum2), có hai loại thay vì bốn.

Điều đó hoàn toàn có thể làm được nếu bạn đã quen thuộc với mẹo thứ tư.

Mẹo 4:Các đề xuất nói trên áp dụng cho thứ tự xuất hiện của các hàm cửa sổ trong mã, ngay cả khi trong một biểu thức bảng được đặt tên, chẳng hạn như CTE hoặc dạng xem và ngay cả khi truy vấn bên ngoài trả về các cột theo thứ tự khác với trong biểu thức bảng được đặt tên. Do đó, nếu bạn cần trả về các cột theo một thứ tự nhất định trong đầu ra và nó khác với thứ tự tối ưu về việc giảm thiểu sắp xếp trong kế hoạch, hãy làm theo các mẹo về thứ tự xuất hiện trong một biểu thức bảng đã đặt tên và trả về các cột trong truy vấn bên ngoài theo thứ tự đầu ra mong muốn.

Truy vấn sau đây, mà tôi sẽ gọi là Truy vấn 5, minh họa kỹ thuật này:

WITH C AS
(
  SELECT orderid, orderdate, custid,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
    SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
  FROM dbo.Orders  
)
SELECT orderid, orderdate, custid,
  sum2, sum1, sum3
FROM C
ORDER BY custid, orderid;

Kế hoạch cho truy vấn này được thể hiện trong Hình 5.

Hình 5:Kế hoạch cho Truy vấn 5

Bạn vẫn chỉ nhận được hai loại trong kế hoạch mặc dù thực tế là thứ tự cột trong đầu ra là:cols khác, sum2, sum1, sum3, giống như trong Truy vấn 2.

Một lưu ý đối với thủ thuật này với biểu thức bảng được đặt tên là nếu các cột của bạn trong biểu thức bảng không được tham chiếu bởi truy vấn bên ngoài, chúng sẽ bị loại trừ khỏi kế hoạch và do đó không được tính.

Hãy xem xét truy vấn sau, mà tôi sẽ gọi là Truy vấn 6:

WITH C AS
(
  SELECT orderid, orderdate, custid,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
    MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
    AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
    SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3
 
  FROM dbo.Orders  
)
SELECT orderid, orderdate, custid,
  sum2, sum1, sum3,
  max2, max1, max3,
  avg2, avg1, avg3
FROM C
ORDER BY custid, orderid;

Tại đây, tất cả các cột biểu thức bảng đều được tham chiếu bởi truy vấn bên ngoài, do đó, việc tối ưu hóa xảy ra dựa trên sự xuất hiện riêng biệt đầu tiên của mỗi nhu cầu sắp xếp trong biểu thức bảng:

  • max1:custid, orderdate, orderid
  • max3:orderid
  • max2:custid, orderid

Điều này dẫn đến một kế hoạch chỉ có hai loại như thể hiện trong Hình 6.

Hình 6:Kế hoạch cho Truy vấn 6

Bây giờ chỉ thay đổi truy vấn bên ngoài bằng cách xóa các tham chiếu đến max2, max1, max3, avg2, avg1 và avg3, như sau:

WITH C AS
(
  SELECT orderid, orderdate, custid,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
    MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
    AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
    SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3
 
  FROM dbo.Orders  
)
SELECT orderid, orderdate, custid,
  sum2, sum1, sum3
FROM C
ORDER BY custid, orderid;

Tôi sẽ gọi truy vấn này là Truy vấn 7. Các phép tính max1, max3, max2, avg1, avg3 và avg2 trong biểu thức bảng không liên quan đến truy vấn bên ngoài nên chúng bị loại trừ. Các phép tính còn lại liên quan đến các hàm cửa sổ trong biểu thức bảng, có liên quan đến truy vấn bên ngoài, là các phép tính của sum2, sum1 và sum3. Thật không may, chúng không xuất hiện trong biểu thức bảng theo thứ tự tối ưu khi sắp xếp tối thiểu. Như bạn có thể thấy trong sơ đồ cho truy vấn này như thể hiện trong Hình 7, có bốn loại.

Hình 7:Kế hoạch cho Truy vấn 7

Nếu bạn đang nghĩ rằng không chắc bạn sẽ có các cột trong truy vấn bên trong mà bạn sẽ không đề cập đến trong truy vấn bên ngoài, hãy nghĩ đến lượt xem. Mỗi khi bạn truy vấn một dạng xem, bạn có thể quan tâm đến một tập hợp con khác nhau của các cột. Với lưu ý này, mẹo thứ năm có thể giúp giảm bớt các loại trong kế hoạch.

Mẹo 5:Trong truy vấn bên trong của biểu thức bảng được đặt tên như CTE hoặc dạng xem, hãy nhóm tất cả các hàm cửa sổ có cùng nhu cầu sắp xếp lại với nhau và làm theo mẹo 1 và 2 theo thứ tự của các nhóm hàm.

Đoạn mã sau triển khai một chế độ xem dựa trên đề xuất này:

CREATE OR ALTER VIEW dbo.MyView
AS
 
SELECT orderid, orderdate, custid,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
  MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
  AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY ordered ROWS UNBOUNDED PRECEDING) AS sum2
 
FROM dbo.Orders;
GO

Bây giờ truy vấn chế độ xem chỉ yêu cầu các cột kết quả được cửa sổ sum2, sum1 và sum3, theo thứ tự sau:

SELECT orderid, orderdate, custid,
  sum2, sum1, sum3
FROM dbo.MyView
ORDER BY custid, orderid;

Tôi sẽ gọi truy vấn này là Truy vấn 8. Bạn nhận được sơ đồ được hiển thị trong Hình 8 với chỉ hai loại.

Hình 8:Kế hoạch cho Truy vấn 8

Mẹo 6

Khi bạn có một truy vấn với nhiều chức năng cửa sổ với nhiều nhu cầu sắp xếp riêng biệt, thông thường là bạn chỉ có thể hỗ trợ một trong số chúng với dữ liệu được sắp xếp trước thông qua một chỉ mục. Đây là trường hợp ngay cả khi tất cả các chức năng cửa sổ đều có các chỉ mục hỗ trợ tương ứng.

Hãy để tôi chứng minh điều này. Nhớ lại trước đó khi bạn tạo chỉ mục idx_nc_cid_od_oid, chỉ mục này có thể hỗ trợ các hàm cửa sổ cần dữ liệu được sắp xếp theo custid, orderdate, orderid, chẳng hạn như biểu thức sau:

SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING)

Giả sử, ngoài hàm cửa sổ này, bạn cũng cần hàm cửa sổ sau trong cùng một truy vấn:

SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING)

Chức năng cửa sổ này sẽ được hưởng lợi từ chỉ mục sau:

CREATE UNIQUE NONCLUSTERED INDEX idx_nc_cid_oid ON dbo.Orders(custid, orderid);

Truy vấn sau, tôi sẽ gọi là Truy vấn 9, gọi cả hai hàm cửa sổ:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
FROM dbo.Orders;

Kế hoạch cho truy vấn này được thể hiện trong Hình 9.

Hình 9:Kế hoạch cho Truy vấn 9

Tôi nhận được thống kê thời gian sau cho truy vấn này trên máy của mình, với kết quả bị loại bỏ trong SSMS:

CPU time = 3234 ms,  elapsed time = 3354 ms.

Như đã giải thích trước đó, SQL Server quét các biểu thức trong cửa sổ theo thứ tự xuất hiện trong truy vấn và tìm ra nó có thể hỗ trợ lần đầu tiên bằng cách quét theo thứ tự chỉ mục idx_nc_cid_od_oid. Nhưng sau đó nó thêm toán tử Sắp xếp vào kế hoạch để sắp xếp dữ liệu giống như chức năng cửa sổ thứ hai cần. Điều này có nghĩa là kế hoạch có tỷ lệ N log N. Nó không xem xét sử dụng chỉ mục idx_nc_cid_oid để hỗ trợ chức năng cửa sổ thứ hai. Có thể bạn đang nghĩ rằng điều đó là không thể, nhưng hãy thử suy nghĩ một chút. Bạn có thể không tính toán từng hàm cửa sổ dựa trên thứ tự chỉ mục tương ứng của nó và sau đó nối kết quả không? Về mặt lý thuyết, bạn có thể và tùy thuộc vào kích thước của dữ liệu, tính khả dụng của lập chỉ mục và các tài nguyên khác có sẵn, phiên bản nối đôi khi có thể hoạt động tốt hơn. SQL Server không xem xét cách tiếp cận này, nhưng bạn chắc chắn có thể triển khai nó bằng cách tự viết phép nối, như sau:

WITH C1 AS
(
  SELECT orderid, orderdate, custid,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1
 
  FROM dbo.Orders
),
C2 AS
(
  SELECT orderid, custid,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
  FROM dbo.Orders
)
SELECT C1.orderid, C1.orderdate, C1.custid, C1.sum1, C2.sum2
FROM C1
  INNER JOIN C2
    ON C1.orderid = C2.orderid;

Tôi sẽ gọi truy vấn này là Truy vấn 10. Kế hoạch cho truy vấn này được hiển thị trong Hình 10.

Hình 10:Kế hoạch cho Truy vấn 10

Kế hoạch sử dụng các bản quét có thứ tự của hai chỉ mục mà không có sự phân loại rõ ràng nào, tính toán các chức năng của cửa sổ và sử dụng một phép nối băm để kết hợp các kết quả. Kế hoạch này chia tỷ lệ tuyến tính so với kế hoạch trước đó có tỷ lệ N log N.

Tôi nhận được thống kê thời gian sau cho truy vấn này trên máy của mình (một lần nữa với kết quả bị loại bỏ trong SSMS):

CPU time = 1000 ms,  elapsed time = 1100 ms.

Tóm lại, đây là mẹo thứ sáu của chúng tôi.

Mẹo 6:Khi bạn có nhiều chức năng cửa sổ với nhiều nhu cầu sắp xếp riêng biệt và bạn có thể hỗ trợ tất cả chúng bằng chỉ mục, hãy thử một phiên bản kết hợp và so sánh hiệu suất của nó với truy vấn không có liên kết.

Dọn dẹp

Nếu bạn đã tắt chế độ song song bằng cách đặt tùy chọn cấu hình trong phạm vi cơ sở dữ liệu MAXDOP thành 1, hãy kích hoạt lại chế độ song song bằng cách đặt nó thành 0:

ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 0;

Nếu bạn đã sử dụng tùy chọn phiên không có tài liệu DBCC OPTIMIZER_WHATIF với tùy chọn CPU được đặt thành 1, hãy kích hoạt lại chế độ song song bằng cách đặt nó thành 0:

DBCC OPTIMIZER_WHATIF(CPUs, 0);

Bạn có thể thử lại tất cả các ví dụ đã bật tính năng song song nếu muốn.

Sử dụng mã sau để xóa các chỉ mục mới bạn đã tạo:

DROP INDEX IF EXISTS idx_nc_cid_od_oid ON dbo.Orders;
DROP INDEX IF EXISTS idx_nc_cid_oid ON dbo.Orders;

Và mã sau để xóa chế độ xem:

DROP VIEW IF EXISTS dbo.MyView;

Thực hiện theo các Mẹo để Giảm thiểu Số lượng Sắp xếp

Các chức năng của cửa sổ cần xử lý dữ liệu đầu vào được sắp xếp theo thứ tự. Lập chỉ mục có thể giúp loại bỏ việc sắp xếp trong kế hoạch, nhưng thông thường chỉ cho một nhu cầu sắp xếp riêng biệt. Các truy vấn có nhiều nhu cầu đặt hàng thường liên quan đến một số loại trong kế hoạch của họ. Tuy nhiên, bằng cách làm theo một số mẹo nhất định, bạn có thể giảm thiểu số lượng sắp xếp cần thiết. Dưới đây là tóm tắt về các mẹo mà tôi đã đề cập trong bài viết này:

  • Mẹo 1: Nếu bạn có một chỉ mục để hỗ trợ một số hàm cửa sổ trong truy vấn, hãy chỉ định những hàm đó trước.
  • Mẹo 2: Nếu truy vấn liên quan đến các chức năng cửa sổ với nhu cầu sắp xếp tương tự như thứ tự trình bày trong truy vấn, hãy chỉ định các chức năng đó sau cùng.
  • Mẹo 3: Đảm bảo thực hiện theo mẹo 1 và 2 cho lần xuất hiện đầu tiên của từng nhu cầu đặt hàng riêng biệt. Các lần xuất hiện tiếp theo của cùng một nhu cầu thứ tự, ngay cả khi không liền kề, được xác định và nhóm lại cùng với nhu cầu đầu tiên.
  • Mẹo 4: Các đề xuất nói trên áp dụng cho thứ tự xuất hiện của các hàm cửa sổ trong mã, ngay cả khi trong một biểu thức bảng được đặt tên, chẳng hạn như CTE hoặc dạng xem, và ngay cả khi truy vấn bên ngoài trả về các cột theo thứ tự khác với trong biểu thức bảng được đặt tên. Do đó, nếu bạn cần trả về các cột theo một thứ tự nhất định trong đầu ra và nó khác với thứ tự tối ưu về việc giảm thiểu sắp xếp trong kế hoạch, hãy làm theo các mẹo về thứ tự xuất hiện trong một biểu thức bảng đã đặt tên và trả về các cột trong truy vấn bên ngoài theo thứ tự đầu ra mong muốn.
  • Mẹo 5: Trong truy vấn bên trong của biểu thức bảng được đặt tên như CTE hoặc dạng xem, hãy nhóm tất cả các hàm cửa sổ có cùng nhu cầu sắp xếp lại với nhau và thực hiện theo mẹo 1 và 2 theo thứ tự của các nhóm hàm.
  • Mẹo 6: Khi bạn có nhiều chức năng cửa sổ với nhiều nhu cầu sắp xếp riêng biệt và bạn có thể hỗ trợ tất cả chúng bằng chỉ mục, hãy thử một phiên bản kết hợp và so sánh hiệu suất của nó với truy vấn không có liên kết.

  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ác chỉ số chính của thiết kế vấn đề

  2. Tối ưu hóa các truy vấn cập nhật

  3. Các cột chỉ mục mới nên nằm trong khóa, hay được bao gồm?

  4. Chạy các tác vụ bảo trì cơ sở dữ liệu SQL bằng SQLCMD

  5. SQL MIN () cho người mới bắt đầu