Heh ... xin lỗi vì tôi trả lời bài viết cũ quá muộn. Và, đúng vậy, tôi phải trả lời vì câu trả lời phổ biến nhất (vào thời điểm đó, câu trả lời CTE đệ quy với liên kết đến 14 phương pháp khác nhau) trên chủ đề này là, ummm ... hiệu suất được thử thách nhiều nhất.
Đầu tiên, bài viết với 14 giải pháp khác nhau phù hợp để xem các phương pháp khác nhau để tạo bảng Numbers / Tally nhanh chóng nhưng như đã chỉ ra trong bài viết và trong chuỗi được trích dẫn, có một rất trích dẫn quan trọng ...
"Các đề xuất liên quan đến hiệu quả và hiệu suất thường mang tính chủ quan. Bất kể cách truy vấn đang được sử dụng, thiết bị triển khai vật lý sẽ xác định hiệu quả của truy vấn. Do đó, thay vì dựa vào các nguyên tắc thiên vị, bạn bắt buộc phải kiểm tra truy vấn và xác định truy vấn nào hoạt động tốt hơn."
Trớ trêu thay, bản thân bài báo chứa nhiều tuyên bố chủ quan và "hướng dẫn thiên vị", chẳng hạn như "một CTE đệ quy có thể tạo ra một danh sách số khá hiệu quả " và "Đây là một phương pháp hiệu quả sử dụng vòng lặp WHILE từ một nhóm tin đăng bởi Itzik Ben-Gen " (mà tôi chắc rằng anh ấy đăng lên chỉ nhằm mục đích so sánh). Thôi nào các bạn ơi ... Chỉ cần nhắc đến cái tên hay của Itzik thôi cũng có thể khiến một số kẻ lười biếng thực sự sử dụng phương pháp kinh khủng đó. Tác giả nên thực hành (những) điều anh ấy giảng và nên thực hiện một thử nghiệm hiệu suất nhỏ trước khi đưa ra những tuyên bố không chính xác đến mức nực cười như vậy, đặc biệt là khi đối mặt với bất kỳ khả năng mở rộng nào.
Với suy nghĩ thực sự thực hiện một số thử nghiệm trước khi đưa ra bất kỳ tuyên bố chủ quan nào về những gì bất kỳ mã nào làm hoặc những gì ai đó "thích", đây là một số mã bạn có thể thực hiện thử nghiệm của riêng mình. Thiết lập hồ sơ cho SPID mà bạn đang chạy thử nghiệm từ đó và tự mình kiểm tra ... chỉ cần thực hiện "Tìm kiếm'n'Replace" của số 1000000 cho số "yêu thích" của bạn và xem ...
--===== Test for 1000000 rows ==================================
GO
--===== Traditional RECURSIVE CTE method
WITH Tally (N) AS
(
SELECT 1 UNION ALL
SELECT 1 + N FROM Tally WHERE N < 1000000
)
SELECT N
INTO #Tally1
FROM Tally
OPTION (MAXRECURSION 0);
GO
--===== Traditional WHILE LOOP method
CREATE TABLE #Tally2 (N INT);
SET NOCOUNT ON;
DECLARE @Index INT;
SET @Index = 1;
WHILE @Index <= 1000000
BEGIN
INSERT #Tally2 (N)
VALUES (@Index);
SET @Index = @Index + 1;
END;
GO
--===== Traditional CROSS JOIN table method
SELECT TOP (1000000)
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS N
INTO #Tally3
FROM Master.sys.All_Columns ac1
CROSS JOIN Master.sys.ALL_Columns ac2;
GO
--===== Itzik's CROSS JOINED CTE method
WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
E02(N) AS (SELECT 1 FROM E00 a, E00 b),
E04(N) AS (SELECT 1 FROM E02 a, E02 b),
E08(N) AS (SELECT 1 FROM E04 a, E04 b),
E16(N) AS (SELECT 1 FROM E08 a, E08 b),
E32(N) AS (SELECT 1 FROM E16 a, E16 b),
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32)
SELECT N
INTO #Tally4
FROM cteTally
WHERE N <= 1000000;
GO
--===== Housekeeping
DROP TABLE #Tally1, #Tally2, #Tally3, #Tally4;
GO
Trong khi chúng tôi đang ở đó, đây là những con số tôi nhận được từ SQL Profiler cho các giá trị 100, 1000, 10000, 100000 và 1000000 ...
SPID TextData Dur(ms) CPU Reads Writes
---- ---------------------------------------- ------- ----- ------- ------
51 --===== Test for 100 rows ============== 8 0 0 0
51 --===== Traditional RECURSIVE CTE method 16 0 868 0
51 --===== Traditional WHILE LOOP method CR 73 16 175 2
51 --===== Traditional CROSS JOIN table met 11 0 80 0
51 --===== Itzik's CROSS JOINED CTE method 6 0 63 0
51 --===== Housekeeping DROP TABLE #Tally 35 31 401 0
51 --===== Test for 1000 rows ============= 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 47 47 8074 0
51 --===== Traditional WHILE LOOP method CR 80 78 1085 0
51 --===== Traditional CROSS JOIN table met 5 0 98 0
51 --===== Itzik's CROSS JOINED CTE method 2 0 83 0
51 --===== Housekeeping DROP TABLE #Tally 6 15 426 0
51 --===== Test for 10000 rows ============ 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 434 344 80230 10
51 --===== Traditional WHILE LOOP method CR 671 563 10240 9
51 --===== Traditional CROSS JOIN table met 25 31 302 15
51 --===== Itzik's CROSS JOINED CTE method 24 0 192 15
51 --===== Housekeeping DROP TABLE #Tally 7 15 531 0
51 --===== Test for 100000 rows =========== 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 4143 3813 800260 154
51 --===== Traditional WHILE LOOP method CR 5820 5547 101380 161
51 --===== Traditional CROSS JOIN table met 160 140 479 211
51 --===== Itzik's CROSS JOINED CTE method 153 141 276 204
51 --===== Housekeeping DROP TABLE #Tally 10 15 761 0
51 --===== Test for 1000000 rows ========== 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 41349 37437 8001048 1601
51 --===== Traditional WHILE LOOP method CR 59138 56141 1012785 1682
51 --===== Traditional CROSS JOIN table met 1224 1219 2429 2101
51 --===== Itzik's CROSS JOINED CTE method 1448 1328 1217 2095
51 --===== Housekeeping DROP TABLE #Tally 8 0 415 0
Như bạn có thể thấy, phương pháp CTE đệ quy kém thứ hai chỉ sau Vòng lặp While cho Thời lượng và CPU và có áp suất bộ nhớ ở dạng đọc logic cao gấp 8 lần so với Vòng lặp Trong khi . Đó là RBAR trên steroid và nên tránh, bằng mọi giá, đối với bất kỳ tính toán hàng đơn lẻ nào cũng như Vòng lặp Trong khi nên tránh. Có những nơi mà đệ quy khá có giá trị nhưng đây KHÔNG PHẢI là một trong số chúng .
Với tư cách là một thanh phụ, ông Denny hoàn toàn có mặt trên ... một bảng Số vĩnh viễn hoặc Bảng đếm có kích thước chính xác là cách để áp dụng cho hầu hết mọi thứ. Kích thước chính xác có nghĩa là gì? Hầu hết mọi người sử dụng bảng Tally để tạo ngày tháng hoặc để thực hiện phân chia trên VARCHAR (8000). Nếu bạn tạo một bảng Tally 11.000 hàng với chỉ số được nhóm chính xác trên "N", bạn sẽ có đủ hàng để tạo ra các ngày có giá trị hơn 30 năm (Tôi làm việc với các khoản thế chấp một chút nên 30 năm là một con số quan trọng đối với tôi ) và chắc chắn đủ để xử lý phân tách VARCHAR (8000). Tại sao "kích thước phù hợp" lại quan trọng như vậy? Nếu bảng Tally được sử dụng nhiều, nó dễ dàng nằm gọn trong bộ nhớ đệm, giúp nó hoạt động nhanh chóng mà không gây nhiều áp lực cho bộ nhớ.
Cuối cùng nhưng không kém phần quan trọng, mọi người đều biết rằng nếu bạn tạo một bảng Tally vĩnh viễn, bạn sử dụng phương pháp nào để tạo nó không quan trọng vì 1) nó chỉ được tạo một lần và 2) nếu nó giống như một hàng 11.000 bảng, tất cả các phương thức sẽ chạy "đủ tốt". Vậy tại sao tất cả sự phẫn nộ về phía tôi về việc sử dụng phương pháp nào ???
Câu trả lời là một số anh chàng / cô gái nghèo không biết gì tốt hơn và chỉ cần hoàn thành công việc của mình có thể thấy một cái gì đó giống như phương pháp CTE đệ quy và quyết định sử dụng nó cho một thứ gì đó lớn hơn và được sử dụng thường xuyên hơn nhiều so với việc xây dựng bảng Tally vĩnh viễn và tôi đang cố gắng bảo vệ những người đó, máy chủ mà mã của họ chạy và công ty sở hữu dữ liệu trên những máy chủ đó . Phải ... đó là một vấn đề lớn. Nó cũng nên dành cho tất cả những người khác. Dạy cách làm đúng thay vì "đủ tốt". Thực hiện một số thử nghiệm trước khi đăng hoặc sử dụng nội dung nào đó từ một bài đăng hoặc cuốn sách ... trên thực tế, cuộc sống mà bạn tiết kiệm được có thể là của riêng bạn, đặc biệt nếu bạn nghĩ CTE đệ quy là cách để thực hiện một cái gì đó như thế này.;-)
Cảm ơn đã lắng nghe ...