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

Quần đảo T-SQL Challenge

Gần đây, tôi đã được người bạn Erland Sommarskog giới thiệu về một thử thách đảo mới. Nó dựa trên một câu hỏi từ một diễn đàn cơ sở dữ liệu công khai. Bản thân thách thức không phức tạp để xử lý khi sử dụng các kỹ thuật nổi tiếng, chủ yếu sử dụng các chức năng cửa sổ. Tuy nhiên, các kỹ thuật này yêu cầu phân loại rõ ràng mặc dù có một chỉ mục hỗ trợ. Điều này ảnh hưởng đến khả năng mở rộng và thời gian phản hồi của các giải pháp. Thích những thách thức, tôi bắt đầu tìm giải pháp để giảm thiểu số lượng toán tử Sắp xếp rõ ràng trong kế hoạch, hoặc tốt hơn, loại bỏ hoàn toàn nhu cầu về những toán tử đó. Và tôi đã tìm thấy một giải pháp như vậy.

Tôi sẽ bắt đầu bằng cách trình bày một dạng tổng quát của thử thách. Sau đó, tôi sẽ đưa ra hai giải pháp dựa trên các kỹ thuật đã biết, tiếp theo là giải pháp mới. Cuối cùng, tôi sẽ so sánh hiệu suất của các giải pháp khác nhau.

Tôi khuyên bạn nên thử tìm giải pháp trước khi triển khai giải pháp của tôi.

Thử thách

Tôi sẽ trình bày một dạng tổng quát về thách thức các hòn đảo ban đầu của Erland.

Sử dụng mã sau để tạo một bảng có tên T1 và điền vào bảng đó với một tập hợp nhỏ dữ liệu mẫu:

 ĐẶT SỐ TÀI KHOẢN BẬT; SỬ DỤNG tempdb; DROP TABLE IF tồn tại dbo.T1 TẠO BẢNG dbo.T1 (grp VARCHAR (10) NOT NULL, ord INT NOT NULL, val VARCHAR (10) NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY (grp, ord)); ĐI CHÈN VÀO dbo. T1 (grp, ord, val) VALUES ('Nhóm A', 1002, 'Y'), ('Nhóm A', 1003, 'Y'), ('Nhóm A', 1005, 'Y'), (' Nhóm A ', 1007,' N '), (' Nhóm A ', 1011,' N '), (' Nhóm A ', 1013,' N '), (' Nhóm A ', 1017,' Y '), ('Nhóm A', 1019, 'Y'), ('Nhóm A', 1023, 'N'), ('Nhóm A', 1029, 'N'), ('Nhóm B', 1001, 'X' ), ('Nhóm B', 1002, 'X'), ('Nhóm B', 1003, 'Z'), ('Nhóm B', 1005, 'Z'), ('Nhóm B', 1008, ' Z '), (' Nhóm B ', 1013,' Z '), (' Nhóm B ', 1021,' Y '), (' Nhóm B ', 1034,' Y '); 

Thử thách như sau:

Giả sử phân vùng dựa trên cột grp và sắp xếp thứ tự dựa trên cột ord, hãy tính số hàng tuần tự bắt đầu bằng 1 trong mỗi nhóm hàng liên tiếp có cùng giá trị trong cột val. Sau đây là kết quả mong muốn cho tập dữ liệu mẫu nhỏ đã cho:

 grp ord val seqno -------- ----- ---- ------ Nhóm A 1002 Y 1 Nhóm A 1003 Y 2 Nhóm A 1005 Y Nhóm A 1007 N 1 Nhóm A 1011 N 2Nhóm A 1013 N Nhóm 3G A 1017 Y 1Nhóm A 1019 Y 2Nhóm A 1023 N 1Nhóm A 1029 N 2Nhóm B 1001 X 1Nhóm B 1002 X 2Nhóm B 1003 Z 1Nhóm B 1005 Z 2Nhóm B 1008 Z Nhóm 3GNhóm B 1013 Z 4GNhóm B 1021 Y 1Nhóm B 1034 Y 2 

Lưu ý định nghĩa của ràng buộc khóa chính dựa trên khóa tổng hợp (grp, ord), dẫn đến một chỉ mục được phân nhóm dựa trên cùng một khóa. Chỉ mục này có thể hỗ trợ các hàm cửa sổ được phân vùng theo grp và sắp xếp theo thứ tự.

Để kiểm tra hiệu suất của giải pháp, bạn sẽ cần điền vào bảng khối lượng dữ liệu mẫu lớn hơn. Sử dụng mã sau để tạo hàm trợ giúp GetNums:

 TẠO CHỨC NĂNG dbo.GetNums (@low AS BIGINT =1, @high AS BIGINT) BẢNG TRỞ LẠI CÓ L0 AS (CHỌN 1 NHƯ c TỪ (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) NHƯ D (c)), L1 AS (CHỌN 1 AS c TỪ L0 AS A CROSS JOIN L0 AS B), L2 AS (CHỌN 1 AS c TỪ L1 AS A CROSS JOIN L1 AS B), L3 AS (CHỌN 1 AS c TỪ L2 NHƯ CHÉO THAM GIA L2 NHƯ B), Nums NHƯ (CHỌN ROW_NUMBER () HẾT (ĐẶT HÀNG BẰNG (CHỌN NULL)) NHƯ rownum TỪ L3) CHỌN ĐẦU (@high - @low + 1) rownum AS rn, @high + 1 - rownum AS op, @low - 1 + rownum AS n TỪ Nums ĐẶT HÀNG BỞI rownum; ĐI 

Sử dụng mã sau để điền T1, sau khi thay đổi các tham số đại diện cho số lượng nhóm, số hàng trên mỗi nhóm và số lượng các giá trị riêng biệt như bạn muốn:

 DECLARE @numgroups AS INT =1000, @rowspergroup AS INT =10000, - kiểm tra với 1000 đến 10000 tại đây @uniquevalues ​​AS INT =5; ALTER TABLE dbo.T1 DROP CONSTRAINT PK_T1; BẢNG TRUNCATE dbo.T1; CHÈN VÀO dbo.T1 VỚI (TABLOCK) (grp, ord, val) CHỌN CAST (G.n AS VARCHAR (10)) AS grp, CAST (R.n AS INT) AS ord, CAST (ABS (CHECKSUM (NEWID ()))% @uniquevalues ​​+ 1 AS VARCHAR (10)) AS val FROM dbo.GetNums (1, @numgroups) AS G CROSS JOIN dbo.GetNums (1, @rowspergroup) AS R; ALTER TABLE dbo.T1 ADD CONSTRAINT PK_T1 PRIMARY KEY ĐƯỢC ĐIỀU CHỈNH (grp, ord); 

Trong các bài kiểm tra hiệu suất của mình, tôi đã điền bảng với 1.000 nhóm, từ 1.000 đến 10.000 hàng mỗi nhóm (vì vậy 1 triệu đến 10 triệu hàng) và 5 giá trị khác biệt. Tôi đã sử dụng SELECT INTO để ghi kết quả vào một bảng tạm thời.

Máy kiểm tra của tôi có bốn CPU logic, đang chạy SQL Server 2019 Enterprise.

Tôi sẽ giả sử bạn đang sử dụng một môi trường được thiết kế để hỗ trợ chế độ hàng loạt trên cửa hàng hàng trực tiếp, ví dụ:sử dụng phiên bản SQL Server 2019 Enterprise giống như của tôi hoặc gián tiếp, bằng cách tạo chỉ mục cột giả trên bảng.

Hãy nhớ rằng, sẽ có thêm điểm nếu bạn tìm ra giải pháp hiệu quả mà không cần phân loại rõ ràng trong kế hoạch. Chúc bạn thành công!

Có cần toán tử Sắp xếp để tối ưu hóa các chức năng cửa sổ không?

Trước khi tôi đề cập đến các giải pháp, hãy tìm hiểu một chút kiến ​​thức cơ bản về tối ưu hóa để những gì bạn sẽ thấy trong các kế hoạch truy vấn sau này sẽ có ý nghĩa hơn.

Các kỹ thuật phổ biến nhất để giải quyết các nhiệm vụ về đảo như của chúng tôi liên quan đến việc sử dụng một số kết hợp các hàm tổng hợp và / hoặc cửa sổ xếp hạng. SQL Server có thể xử lý các chức năng cửa sổ như vậy bằng cách sử dụng một loạt các toán tử chế độ hàng cũ hơn (Phân đoạn, Dự án trình tự, Phân đoạn, Bộ đệm cửa sổ, Tổng hợp luồng) hoặc toán tử Chế độ hàng loạt mới hơn và thường hiệu quả hơn.

Trong cả hai trường hợp, các toán tử xử lý tính toán của hàm cửa sổ cần nhập dữ liệu được sắp xếp theo thứ tự của các phần tử phân vùng cửa sổ và sắp xếp. Nếu bạn không có chỉ mục hỗ trợ, SQL Server đương nhiên sẽ cần giới thiệu toán tử Sắp xếp trong kế hoạch. Ví dụ:nếu bạn có nhiều chức năng cửa sổ trong giải pháp của mình với nhiều hơn một sự kết hợp duy nhất giữa phân vùng và sắp xếp, bạn nhất định phải sắp xếp rõ ràng trong kế hoạch. Nhưng điều gì sẽ xảy ra nếu bạn chỉ có một kết hợp duy nhất giữa phân vùng và sắp xếp và một chỉ mục hỗ trợ?

Phương thức chế độ hàng cũ hơn có thể dựa vào dữ liệu được sắp xếp trước được nhập từ một chỉ mục mà không cần toán tử Sắp xếp rõ ràng trong cả chế độ nối tiếp và song song. Nhà điều hành chế độ hàng loạt mới hơn loại bỏ phần lớn sự kém hiệu quả của việc tối ưu hóa chế độ hàng cũ hơn và có những lợi ích vốn có của việc xử lý chế độ hàng loạt. Tuy nhiên, việc triển khai song song hiện tại của nó yêu cầu một toán tử Sắp xếp song song theo chế độ hàng loạt trung gian ngay cả khi có chỉ mục hỗ trợ. Chỉ việc triển khai nối tiếp của nó có thể dựa vào thứ tự chỉ mục mà không cần toán tử Sắp xếp. Đây là tất cả để nói khi trình tối ưu hóa cần chọn một chiến lược để xử lý chức năng cửa sổ của bạn, giả sử bạn có một chỉ mục hỗ trợ, nó thường sẽ là một trong bốn tùy chọn sau:

  1. Chế độ hàng, nối tiếp, không sắp xếp
  2. Chế độ hàng, song song, không sắp xếp
  3. Chế độ hàng loạt, nối tiếp, không phân loại
  4. Chế độ hàng loạt, song song, sắp xếp

Bất kỳ kết quả nào dẫn đến chi phí kế hoạch thấp nhất sẽ được chọn, giả sử tất nhiên các điều kiện tiên quyết cho chế độ song song và chế độ hàng loạt được đáp ứng khi xem xét các điều kiện đó. Thông thường, để trình tối ưu hóa biện minh cho một kế hoạch song song, lợi ích của tính năng song song cần phải lớn hơn công việc bổ sung như đồng bộ hóa luồng. Với tùy chọn 4 ở trên, lợi ích của tính năng song song cần lớn hơn công việc bổ sung thông thường liên quan đến tính năng song song, cộng với việc sắp xếp bổ sung.

Trong khi thử nghiệm các giải pháp khác nhau cho thách thức của chúng tôi, tôi đã gặp trường hợp trình tối ưu hóa đã chọn tùy chọn 2 ở trên. Nó đã chọn nó mặc dù thực tế là phương pháp chế độ hàng liên quan đến một vài điểm không hiệu quả vì lợi ích song song và không có sự sắp xếp dẫn đến một kế hoạch có chi phí thấp hơn các lựa chọn thay thế. Trong một số trường hợp đó, việc ép buộc một kế hoạch nối tiếp với gợi ý dẫn đến tùy chọn 3 ở trên và có hiệu suất tốt hơn.

Với nền tảng này, chúng ta hãy xem xét các giải pháp. Tôi sẽ bắt đầu với hai giải pháp dựa trên các kỹ thuật đã biết cho các nhiệm vụ trên đảo không thể thoát khỏi phân loại rõ ràng trong kế hoạch.

Giải pháp dựa trên kỹ thuật 1 đã biết

Sau đây là giải pháp đầu tiên cho thách thức của chúng tôi, dựa trên một kỹ thuật đã được biết đến trong một thời gian:

 WITH C AS (SELECT *, ROW_NUMBER () OVER (PARTITION BY grp ORDER BY ord) - ROW_NUMBER () OVER (PARTITION BY grp, val ORDER BY ord) AS đảo FROM dbo.T1) SELECT grp, ord, val , ROW_NUMBER () HẾT (PHẦN THEO grp, val, đảo ĐẶT HÀNG THEO ord) NHƯ seqnoFROM C; 

Tôi sẽ gọi nó là Giải pháp đã biết 1.

CTE được gọi là C dựa trên một truy vấn tính hai số hàng. Đầu tiên (tôi sẽ gọi nó là rn1) được phân vùng theo grp và sắp xếp theo thứ tự của ord. Thứ hai (tôi sẽ gọi nó là rn2) được phân vùng bởi grp và val và được sắp xếp theo thứ tự của ord. Vì rn1 có khoảng trống giữa các nhóm khác nhau có cùng giá trị và rn2 thì không, nên sự khác biệt giữa rn1 và rn2 (cột được gọi là đảo) là một giá trị hằng số duy nhất cho tất cả các hàng có cùng giá trị grp và val. Sau đây là kết quả của truy vấn bên trong, bao gồm kết quả của phép tính số hai hàng, không được trả về dưới dạng các cột riêng biệt:

 grp ord val rn1 rn2 island -------- ----- ---- ---- ---- ------- Nhóm A 1002 Y 1 1 0 Nhóm A 1003 Y 2 2 0 Nhóm A 1005 Y 3 3 0 Nhóm A 1007 N 4 1 3G Nhóm A 1011 N 5 2 3G Nhóm A 1013 N 6 3 3G Nhóm A 1017 Y 7 4 Nhóm 3G Nhóm A 1019 Y 8 5 Nhóm 3G A 1023 N 9 4 5G Nhóm A 1029 N 10 5 5 Nhóm B 1001 X 1 1 0 Nhóm B 1002 X 2 2 0 Nhóm B 1003 Z 3 1 2 Nhóm B 1005 Z 4 2 2 Nhóm B 1008 Z 5 3 2 Nhóm B 1013 Z 6 4 2 Nhóm B 1021 Y 7 1 6 Nhóm B 1034 Y 8 2 6 

Việc còn lại đối với truy vấn bên ngoài phải làm là tính toán seqno cột kết quả bằng cách sử dụng hàm ROW_NUMBER, được phân vùng theo grp, val và island và sắp xếp theo thứ tự, tạo ra kết quả mong muốn.

Lưu ý rằng bạn có thể nhận được cùng một giá trị đảo cho các giá trị val khác nhau trong cùng một phân vùng, chẳng hạn như trong đầu ra ở trên. Đây là lý do tại sao điều quan trọng là sử dụng grp, val và island làm các yếu tố phân vùng cửa sổ chứ không phải chỉ grp và island.

Tương tự, nếu bạn đang xử lý một nhiệm vụ đảo yêu cầu bạn nhóm dữ liệu theo đảo và tính toán tổng hợp nhóm, bạn sẽ nhóm các hàng theo grp, val và island. Nhưng điều này không đúng với thử thách của chúng tôi. Ở đây, bạn được giao nhiệm vụ chỉ tính toán các số hàng một cách độc lập cho mỗi hòn đảo.

Hình 1 có sơ đồ mặc định mà tôi nhận được cho giải pháp này trên máy của mình sau khi điền vào bảng 10 triệu hàng.

Hình 1:Phương án song song cho Giải pháp đã biết 1

Việc tính toán rn1 có thể dựa vào thứ tự chỉ mục. Vì vậy, trình tối ưu hóa đã chọn chiến lược không có sắp xếp + chế độ hàng song song cho chiến lược này (các toán tử Dự án Phân đoạn và Trình tự đầu tiên trong kế hoạch). Vì các tính toán của cả rn2 và seqno sử dụng sự kết hợp độc đáo của riêng chúng giữa các phần tử phân vùng và sắp xếp, nên việc sắp xếp rõ ràng là không thể tránh khỏi đối với những người đó bất kể chiến lược được sử dụng. Vì vậy, trình tối ưu hóa đã chọn chiến lược sắp xếp + chế độ hàng loạt song song cho cả hai. Kế hoạch này liên quan đến hai toán tử Sắp xếp rõ ràng.

Trong bài kiểm tra hiệu suất của tôi, giải pháp này mất 3,68 giây để hoàn thành với 1 triệu hàng và 43,1 giây với 10 triệu hàng.

Như đã đề cập, tôi cũng đã thử nghiệm tất cả các giải pháp bằng cách bắt buộc một kế hoạch nối tiếp (với gợi ý MAXDOP 1). Kế hoạch nối tiếp cho giải pháp này được thể hiện trong Hình 1.

Hình 2:Kế hoạch nối tiếp cho Giải pháp đã biết 1

Như dự kiến, lần tính toán rn1 này cũng sử dụng chiến lược chế độ hàng loạt mà không có toán tử Sắp xếp trước, nhưng kế hoạch vẫn có hai toán tử Sắp xếp cho các phép tính số hàng tiếp theo. Kế hoạch nối tiếp hoạt động kém hơn so với kế hoạch song song trên máy của tôi với tất cả các kích thước đầu vào mà tôi đã thử nghiệm, mất 4,54 giây để hoàn thành với 1 triệu hàng và 61,5 giây với 10 triệu hàng.

Giải pháp dựa trên kỹ thuật 2 đã biết

Giải pháp thứ hai mà tôi sẽ trình bày dựa trên một kỹ thuật tuyệt vời để phát hiện đảo mà tôi đã học được từ Paul White vài năm trước. Sau đây là mã giải pháp hoàn chỉnh dựa trên kỹ thuật này:

 WITH C1 AS (SELECT *, CASE WHEN val =LAG (val) OVER (PARTITION BY grp ORDER BY ord) THEN 0 ELSE 1 END AS isstart FROM dbo.T1), C2 AS (SELECT *, SUM (isstart) HẾT (PHẦN ĐẶT HÀNG THEO grp THEO ĐƯỜNG LỆNH CHƯA ĐƯỢC TỔNG HỢP CHÍNH XÁC) NHƯ đảo TỪ C1) CHỌN grp, ord, val, ROW_NUMBER () HẾT (ĐOẠN PHẦN THEO grp, đảo LỆNH THEO ord) NHƯ seqnoFROM C2; 

Tôi sẽ gọi giải pháp này là Giải pháp đã biết 2.

Truy vấn xác định CTE C1 tính toán sử dụng biểu thức CASE và hàm cửa sổ LAG (được phân vùng theo grp và sắp xếp theo thứ tự) để tính toán cột kết quả được gọi là isstart. Cột này có giá trị 0 khi giá trị val hiện tại giống với giá trị trước đó và 1 nếu không. Nói cách khác, nó là 1 khi hàng là hàng đầu tiên trong một hòn đảo và 0 nếu không.

Sau đây là đầu ra của truy vấn xác định C1:

 grp ord val isstart -------- ----- ---- -------- Nhóm A 1002 Y 1Nhóm A 1003 Y 0Nhóm A 1005 Y 0Nhóm A 1007 N 1Nhóm A 1011 N 0Nhóm A 1013 N 0Nhóm A 1017 Y 1Nhóm A 1019 Y 0Nhóm A 1023 N 1Nhóm A 1029 N 0Nhóm B 1001 X 1Nhóm B 1002 X 0Nhóm B 1003 Z 1Nhóm B 1005 Z 0Nhóm B 1008 Z 0Nhóm B 1013 Z 0Nhóm B 1021 Y 1Nhóm B 1034 Y 0 

Điều kỳ diệu liên quan đến việc phát hiện đảo xảy ra trong CTE C2. Truy vấn xác định nó tính toán một số nhận dạng đảo bằng cách sử dụng hàm cửa sổ SUM (cũng được phân vùng theo grp và sắp xếp theo thứ tự theo ord) được áp dụng cho cột isstart. Cột kết quả với mã định danh đảo được gọi là đảo. Trong mỗi phân vùng, bạn nhận được 1 cho đảo đầu tiên, 2 cho đảo thứ hai, v.v. Vì vậy, sự kết hợp giữa các cột grp và đảo là một mã định danh đảo, bạn có thể sử dụng mã này trong các nhiệm vụ về đảo liên quan đến việc nhóm theo đảo khi có liên quan.

Sau đây là đầu ra của truy vấn xác định C2:

 grp ord val isstart island -------- ----- ---- -------- ------- Nhóm A 1002 Y 1 1 Nhóm A 1003 Y 0 1 Nhóm A 1005 Y 0 1 Nhóm A 1007 N 1 2 Nhóm A 1011 N 0 2 Nhóm A 1013 N 0 2 Nhóm A 1017 Y 1 3G Nhóm A 1019 Y 0 Nhóm A 1023 N 1 4G Nhóm A 1029 N 0 Nhóm B 1001 X 1 1 Nhóm B 1002 X 0 1Nhóm B 1003 Z 1 2Nhóm B 1005 Z 0 2Nhóm B 1008 Z 0 2Nhóm B 1013 Z 0 2Nhóm B 1021 Y 1 3Gnhóm B 1034 Y 0 3 

Cuối cùng, truy vấn bên ngoài tính toán cột kết quả mong muốn seqno với một hàm ROW_NUMBER, được phân vùng theo grp và đảo, và được sắp xếp theo thứ tự. Lưu ý rằng sự kết hợp giữa các phần tử phân vùng và sắp xếp thứ tự này khác với sự kết hợp được sử dụng bởi các hàm cửa sổ trước đó. Trong khi việc tính toán hai chức năng cửa sổ đầu tiên có thể dựa vào thứ tự chỉ mục, chức năng cuối cùng thì không.

Hình 3 có sơ đồ mặc định mà tôi nhận được cho giải pháp này.

Hình 3:Phương án song song cho Giải pháp đã biết 2

Như bạn có thể thấy trong kế hoạch, việc tính toán hai hàm cửa sổ đầu tiên sử dụng chiến lược không có sắp xếp + chế độ hàng song song và tính toán cuối cùng sử dụng chiến lược sắp xếp + chế độ hàng loạt song song.

Thời gian chạy mà tôi nhận được cho giải pháp này dao động từ 2,57 giây đối với 1 triệu hàng đến 46,2 giây đối với 10 triệu hàng.

Khi buộc xử lý nối tiếp, tôi nhận được kế hoạch được hiển thị trong Hình 4.

Hình 4:Kế hoạch nối tiếp cho Giải pháp đã biết 2

Như dự kiến, lần này tất cả các tính toán chức năng cửa sổ đều dựa vào chiến lược chế độ hàng loạt. Hai cái đầu tiên mà không cần sắp xếp trước và cái cuối cùng với. Cả kế hoạch song song và kế hoạch nối tiếp đều liên quan đến một toán tử Sắp xếp rõ ràng. Kế hoạch nối tiếp hoạt động tốt hơn kế hoạch song song trên máy của tôi với các kích thước đầu vào mà tôi đã thử nghiệm. Thời gian chạy mà tôi nhận được đối với kế hoạch nối tiếp bắt buộc dao động từ 1,75 giây so với 1 triệu hàng đến 21,7 giây so với 10 triệu hàng.

Giải pháp dựa trên kỹ thuật mới

Khi Erland giới thiệu thử thách này trong một diễn đàn riêng tư, mọi người đã nghi ngờ về khả năng giải quyết nó bằng một truy vấn đã được tối ưu hóa với một kế hoạch mà không cần phân loại rõ ràng. Đó là tất cả những gì tôi cần nghe để động viên mình. Vì vậy, đây là những gì tôi nghĩ ra:

 VỚI C1 AS (SELECT *, ROW_NUMBER () OVER (PARTITION BY grp ORDER BY ord) AS rn, LAG (val) OVER (PARTITION BY grp ORDER BY ord) AS trước từ dbo.T1), C2 AS (SELECT *, MAX (TRƯỜNG HỢP KHI val =trước ĐẾN NULL ELSE rn KẾT THÚC) HẾT (PHẦN THEO LỆNH Grp THEO ĐƯỜNG LỆNH KHÔNG ĐƯỢC BẮT ĐẦU TỪ ĐẦU TIÊN TỪ C1) CHỌN grp, ord, val, rn - firstrn + 1 AS seqnoFROM C2;  

Giải pháp sử dụng ba hàm cửa sổ:LAG, ROW_NUMBER và MAX. Điều quan trọng ở đây là cả ba đều dựa trên phân vùng grp và thứ tự ord, được căn chỉnh với thứ tự khóa chỉ mục hỗ trợ.

Truy vấn xác định CTE C1 sử dụng hàm ROW_NUMBER để tính số hàng (cột rn) và hàm LAG để trả về giá trị val trước đó (cột trước).

Đây là kết quả của truy vấn xác định C1:

 grp ord val rn before -------- ----- ---- --- ----- Nhóm A 1002 Y 1 NULLNhóm A 1003 Y 2 YGroup A 1005 Y 3 YGroup A 1007 N 4 YGroup A 1011 N 5 NGroup A 1013 N 6 NGroup A 1017 Y 7 NGroup A 1019 Y 8 YGroup A 1023 N 9 YGroup A 1029 N 10 NGroup B 1001 X 1 NULLGroup B 1002 X 2 X Group B 1003 Z 3 XGroup B 1005 Z 4 ZNhóm B 1008 Z 5 ZGroup B 1013 Z 6 ZGroup B 1021 Y 7 ZGroup B 1034 Y 8 Y 

Lưu ý rằng khi val và trước giống nhau, nó không phải là hàng đầu tiên trong đảo, ngược lại thì đúng như vậy.

Dựa trên logic này, truy vấn xác định CTE C2 sử dụng biểu thức CASE trả về rn khi hàng là hàng đầu tiên trong một đảo và NULL nếu không. Sau đó, mã áp dụng chức năng cửa sổ MAX cho kết quả của biểu thức CASE, trả về rn đầu tiên của đảo (cột đầu tiên).

Đây là đầu ra của truy vấn xác định C2, bao gồm đầu ra của biểu thức CASE:

 grp ord val rn pres CASE firstrn -------- ----- ---- --- ----- ----- -------- Nhóm A 1002 Y 1 NULL 1 1Nhóm A 1003 Y 2 Y NULL 1Nhóm A 1005 Y 3 Y NULL 1Nhóm A 1007 N 4 Y 4 4Group A 1011 N 5 N NULL 4Group A 1013 N 6 N NULL 4Group A 1017 Y 7 N 7 7Nhóm A 1019 Y 8 Y KHÔNG ĐỦ 7Nhóm A 1023 N 9 Y 9 9Nhóm A 1029 N 10 N KHÔNG ĐẦY 9Nhóm B 1001 X 1 NULL 1 1Nhóm B 1002 X 2 X NULL 1Nhóm B 1003 Z 3 X 3 Nhóm 3G B 1005 Z 4 Z KHÔNG ĐẦY ĐỦ 3GNhóm B 1008 Z 5 Z KHÔNG ĐỦ 3G Nhóm B 1013 Z 6 Z KHÔNG ĐỦ 3G Nhóm B 1021 Y 7 Z 7 7 Nhóm B 1034 Y 8 Y KHÔNG ĐỦ 7 

Những gì còn lại cho truy vấn bên ngoài là tính toán seqno cột kết quả mong muốn dưới dạng rn trừ đầu tiên cộng với 1.

Hình 5 có sơ đồ song song mặc định mà tôi nhận được cho giải pháp này khi thử nghiệm nó bằng cách sử dụng câu lệnh SELECT INTO, ghi kết quả vào một bảng tạm thời.

Hình 5:Kế hoạch song song cho một giải pháp mới

Không có toán tử sắp xếp rõ ràng nào trong kế hoạch này. Tuy nhiên, tất cả ba hàm cửa sổ đều được tính bằng cách sử dụng chiến lược không có sắp xếp + chế độ hàng song song, vì vậy, chúng tôi đang thiếu các lợi ích của xử lý hàng loạt. Thời gian chạy mà tôi nhận được cho giải pháp này với phương án song song dao động từ 2,47 giây so với 1 triệu hàng và 41,4 so với 10 triệu hàng.

Ở đây, có khả năng khá cao là gói nối tiếp với xử lý hàng loạt sẽ hoạt động tốt hơn đáng kể, đặc biệt là khi máy không có nhiều CPU. Nhớ lại Tôi đang thử nghiệm các giải pháp của mình với một máy có 4 CPU logic. Hình 6 có phương án tôi có cho giải pháp này khi buộc xử lý nối tiếp.

Hình 6:Kế hoạch nối tiếp cho một giải pháp mới

Tất cả ba chức năng cửa sổ đều sử dụng chiến lược không sắp xếp + chế độ hàng loạt nối tiếp. Và kết quả khá ấn tượng. Thời gian chạy của giải pháp này dao động từ 0,5 giây đối với 1 triệu hàng và 5,49 giây đối với 10 triệu hàng. Điều gây tò mò về giải pháp này là khi kiểm tra nó như một câu lệnh SELECT bình thường (với kết quả bị loại bỏ) trái ngược với câu lệnh SELECT INTO, SQL Server đã chọn gói nối tiếp theo mặc định. Với hai giải pháp còn lại, theo mặc định, tôi nhận được một gói song song cả với CHỌN và CHỌN VÀO.

Xem phần tiếp theo để biết kết quả kiểm tra hiệu suất hoàn chỉnh.

So sánh hiệu suất

Đây là mã tôi đã sử dụng để kiểm tra ba giải pháp, tất nhiên là bỏ ghi chú gợi ý MAXDOP 1 để kiểm tra các kế hoạch nối tiếp:

 - Kiểm tra Giải pháp Đã biết 1 BẢNG DROP NẾU TỒN TẠI #Result; VỚI C AS (SELECT *, ROW_NUMBER () OVER (PARTITION BY grp ORDER BY ord) - ROW_NUMBER () OVER (PARTITION BY grp, val ORDER BY ord) AS đảo FROM dbo.T1) SELECT grp, ord, val, ROW_NUMBER ( ) HẾT (PHẦN THEO Grp, val, đảo ĐẶT HÀNG THEO ord) NHƯ seqnoINTO #ResultFROM C / * TÙY CHỌN (MAXDOP 1) * /; - bỏ ghi chú cho thử nghiệm nối tiếpGO - Thử nghiệm Giải pháp đã biết 2 BẢNG DROP NẾU TỒN TẠI #Result; VỚI C1 AS (SELECT *, CASE WHEN val =LAG (val) OVER (PARTITION BY grp ORDER BY ord) THEN 0 ELSE 1 END AS isstart FROM dbo.T1), C2 AS (SELECT *, SUM (isstart) OVER (PARTITION ĐẶT HÀNG THEO grp THEO ĐƯỜNG LỆNH KHÔNG ĐƯỢC BAO GỒM CHÍNH XÁC) NHƯ đảo TỪ C1) CHỌN grp, ord, val, ROW_NUMBER () HẾT (PHẦN THEO Grp, đảo ĐẶT HÀNG THEO ord) NHƯ seqnoINTO #ResultFROM C2 / * TÙY CHỌN (MAXDOP 1) * /; ĐI - Thử nghiệm giải pháp mới BẢNG DROP NẾU TỒN TẠI #Result; VỚI C1 NHƯ (CHỌN *, ROW_NUMBER () HẾT (PHẦN THEO LỆNH Grp THEO ord) NHƯ rn, LAG (val) HẾT (PHẦN BẰNG LỆNH Grp THEO ord) NHƯ trước đây TỪ dbo.T1), C2 AS (CHỌN *, MAX (TRƯỜNG HỢP KHI val =trước ĐẾN NULL ELSE rn KẾT THÚC) HẾT (PHẦN THEO ĐƠN ĐẶT HÀNG Grp BY ord ROWS UNBOUNDED PRECEDING) NHƯ đầu tiên TỪ C1) CHỌN grp, ord, val, rn - firstrn + 1 AS seqnoINTO #ResultFROM C2 / * TÙY CHỌN ( MAXDOP 1) * /; 

Hình 7 có thời gian chạy của cả kế hoạch song song và nối tiếp cho tất cả các giải pháp dựa trên các kích thước đầu vào khác nhau.

Hình 7:So sánh hiệu suất

Giải pháp mới, sử dụng chế độ nối tiếp, là người chiến thắng rõ ràng. Nó có hiệu suất tuyệt vời, mở rộng tuyến tính và thời gian phản hồi ngay lập tức.

Kết luận

Nhiệm vụ hải đảo khá phổ biến trong cuộc sống thực. Nhiều người trong số họ liên quan đến cả việc xác định các đảo và nhóm dữ liệu theo đảo. Thử thách các đảo của Erland, trọng tâm của bài viết này, độc đáo hơn một chút vì nó không liên quan đến việc nhóm mà thay vào đó, sắp xếp các hàng của mỗi hòn đảo với số hàng.

Tôi đã trình bày hai giải pháp dựa trên các kỹ thuật đã biết để xác định các đảo. Vấn đề với cả hai là chúng dẫn đến các kế hoạch liên quan đến phân loại rõ ràng, điều này ảnh hưởng tiêu cực đến hiệu suất, khả năng mở rộng và thời gian phản hồi của các giải pháp. Tôi cũng đã trình bày một kỹ thuật mới dẫn đến một kế hoạch không có sự phân loại nào cả. Kế hoạch nối tiếp cho giải pháp này, sử dụng chiến lược không sắp xếp + chế độ hàng loạt nối tiếp, có hiệu suất tuyệt vời, mở rộng tuyến tính và thời gian phản hồi ngay lập tức. Thật không may, ít nhất là hiện tại, chúng tôi không thể không có chiến lược sắp xếp + chế độ hàng loạt song song để xử lý các chức năng cửa sổ.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Quét đơn hàng phân bổ

  2. SQL, tạo bảng

  3. XEM TRƯỚC:Tiện ích mở rộng SentryOne Plan Explorer dành cho Azure Data Studio

  4. SQL CHỌN SUM

  5. Đồng bộ hóa cấu trúc cơ sở dữ liệu giữa các ứng dụng