Có sự khác biệt về hiệu suất giữa CTE, Truy vấn phụ, Bảng tạm thời hoặc Biến bảng không?


222

Trong câu hỏi SO xuất sắc này , sự khác biệt giữa CTEsub-queriesđã được thảo luận.

Tôi muốn hỏi cụ thể:

Trong trường hợp nào thì mỗi cách sau đây hiệu quả hơn / nhanh hơn?

  • CTE
  • Truy vấn phụ
  • Bảng tạm thời
  • Bảng biến

Theo truyền thống, tôi đã sử dụng rất nhiều temp tablestrong việc phát triển stored procedures- vì chúng dường như dễ đọc hơn nhiều truy vấn phụ đan xen.

Non-recursive CTEs đóng gói các bộ dữ liệu rất tốt và rất dễ đọc, nhưng có những trường hợp cụ thể mà người ta có thể nói rằng chúng sẽ luôn hoạt động tốt hơn không? hoặc đó là một trường hợp phải luôn luôn loay hoay với các tùy chọn khác nhau để tìm ra giải pháp hiệu quả nhất?


BIÊN TẬP

Gần đây tôi đã được thông báo rằng về mặt hiệu quả, các bảng tạm thời là lựa chọn đầu tiên tốt vì chúng có biểu đồ liên quan tức là thống kê.


4
Câu trả lời chung: nó phụ thuộc. Và nó phụ thuộc vào một số nhiều yếu tố, bất kỳ tuyên bố chung nào cũng có khả năng sai - trong một số tình huống. Về cơ bản: bạn cần kiểm tra và đo lường - xem cái nào phù hợp nhất với bạn!
marc_s

@marc_s - ok; Có lẽ câu hỏi này nên được đóng lại vì chủ quan? Lưu ý bạn rất nhiều câu hỏi SQL về SO có thể được đánh giá là chủ quan.
whytheq

1
Nó có thể bị đóng cửa vì quá rộng - và tôi đồng ý với bạn - rất nhiều điều và chủ đề trong SQL thực sự sẽ nhận được câu trả lời về điều đó . Đôi khi, người ta có thể liệt kê hai hoặc ba tiêu chí để đưa ra quyết định, nhưng với câu hỏi của bạn ở đây, bạn không thể đưa ra lời khuyên âm thanh - điều này phụ thuộc rất nhiều - cấu trúc bảng của bạn, dữ liệu trong các bảng đó, các truy vấn bạn đang sử dụng, chiến lược lập chỉ mục của bạn và nhiều hơn nữa ....
marc_s

@marc_s nên thử và giữ lại - có lời khuyên nào về các chỉnh sửa có thể có đối với OP để cố gắng làm cho nó cụ thể và hẹp hơn không?
whytheq

Xin lưu ý câu hỏi này dành riêng cho SQL Server. Đối với các DB khác như postgres, CTE thường chậm hơn nhiều so với các truy vấn con tương đương (xem http://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/ )
Jay

Câu trả lời:


243

SQL là ngôn ngữ khai báo, không phải là ngôn ngữ thủ tục. Đó là, bạn xây dựng một câu lệnh SQL để mô tả các kết quả mà bạn muốn. Bạn không nói cho công cụ SQL biết cách thực hiện công việc.

Theo nguyên tắc chung, nên để công cụ SQL và trình tối ưu hóa SQL tìm ra kế hoạch truy vấn tốt nhất. Có nhiều nỗ lực trong nhiều năm để phát triển một công cụ SQL, vì vậy hãy để các kỹ sư làm những gì họ biết cách làm.

Tất nhiên, có những tình huống mà kế hoạch truy vấn không tối ưu. Sau đó, bạn muốn sử dụng gợi ý truy vấn, cơ cấu lại truy vấn, cập nhật số liệu thống kê, sử dụng bảng tạm thời, thêm chỉ mục, v.v để có hiệu suất tốt hơn.

Đối với câu hỏi của bạn. Về mặt lý thuyết, hiệu năng của CTE và các truy vấn con phải giống nhau vì cả hai đều cung cấp cùng một thông tin cho trình tối ưu hóa truy vấn. Một điểm khác biệt là CTE được sử dụng nhiều lần có thể dễ dàng xác định và tính toán một lần. Các kết quả sau đó có thể được lưu trữ và đọc nhiều lần. Thật không may, SQL Server dường như không tận dụng được phương pháp tối ưu hóa cơ bản này (bạn có thể gọi đây là loại bỏ truy vấn con chung).

Các bảng tạm thời là một vấn đề khác, bởi vì bạn đang cung cấp thêm hướng dẫn về cách chạy truy vấn. Một sự khác biệt chính là trình tối ưu hóa có thể sử dụng số liệu thống kê từ bảng tạm thời để thiết lập kế hoạch truy vấn của nó. Điều này có thể dẫn đến tăng hiệu suất. Ngoài ra, nếu bạn có một CTE (truy vấn con) phức tạp được sử dụng nhiều lần, thì việc lưu trữ nó trong một bảng tạm thời thường sẽ giúp tăng hiệu suất. Truy vấn chỉ được thực hiện một lần.

Câu trả lời cho câu hỏi của bạn là bạn cần phải chơi xung quanh để có được hiệu suất mà bạn mong đợi, đặc biệt đối với các truy vấn phức tạp được chạy thường xuyên. Trong một thế giới lý tưởng, trình tối ưu hóa truy vấn sẽ tìm thấy đường dẫn thực hiện hoàn hảo. Mặc dù nó thường như vậy, bạn có thể tìm ra cách để có hiệu suất tốt hơn.


11
Một số nghiên cứu của Microsoft về các cải tiến có thể có trong tương lai trong lĩnh vực này nằm trong ấn phẩm "Khai thác hiệu quả các biểu hiện tương tự để xử lý truy vấn có sẵn từ đây
Martin Smith

3
Cho rằng bài báo đó đã được trình bày vào năm 2007, có ý tưởng nào cho dù họ đã kết hợp nó trong SQL Server 2012 không?
Gordon Linoff

3
Một câu trả lời tuyệt vời! Chỉ cần nhấn mạnh: SQL là ngôn ngữ khai báo và chúng tôi không kiểm soát CÁCH dữ liệu được kéo. Do đó, hiệu suất / tốc độ thay đổi từ truy vấn để truy vấn.
Simcha Khabinsky

2
@RGS. . . Các chỉ mục trên các bảng tạm thời chắc chắn cải thiện các truy vấn có thể tận dụng các chỉ mục đó - như với các chỉ mục trên một bảng vĩnh viễn. Nhưng, nếu bạn cụ thể hóa một truy vấn con dưới dạng bảng tạm thời, bạn có thể mất lợi thế của các chỉ mục trên các bảng ban đầu.
Gordon Linoff

2
@RGS. . .Khi công cụ cơ sở dữ liệu cụ thể hóa truy vấn con / CTE trong quá trình thực hiện một truy vấn phức tạp, nó không thêm các chỉ mục vào việc cụ thể hóa. Bạn có thể làm điều này bằng tay bằng cách sử dụng các bảng tạm thời.
Gordon Linoff

77

Không có quy tắc. Tôi thấy các CTE dễ đọc hơn và sử dụng chúng trừ khi chúng thể hiện một số vấn đề về hiệu năng, trong trường hợp đó tôi điều tra vấn đề thực tế thay vì đoán rằng CTE là vấn đề và cố gắng viết lại bằng cách sử dụng một cách tiếp cận khác. Vấn đề thường có nhiều vấn đề hơn là cách tôi chọn để tuyên bố ý định của mình với truy vấn.

Chắc chắn có những trường hợp khi bạn có thể làm sáng tỏ CTE hoặc xóa các truy vấn con và thay thế chúng bằng bảng #temp và giảm thời lượng. Điều này có thể do nhiều thứ khác nhau, chẳng hạn như số liệu thống kê cũ, thậm chí không thể có được số liệu thống kê chính xác (ví dụ: tham gia vào hàm có giá trị bảng), song song hoặc thậm chí không thể tạo kế hoạch tối ưu do tính phức tạp của truy vấn ( trong trường hợp phá vỡ nó có thể mang lại cơ hội chiến đấu tối ưu hóa). Nhưng cũng có trường hợp I / O liên quan đến việc tạo bảng #temp có thể vượt trội hơn các khía cạnh hiệu suất khác có thể làm cho hình dạng kế hoạch cụ thể sử dụng CTE kém hấp dẫn.

Thành thật mà nói, có quá nhiều biến để đưa ra câu trả lời "đúng" cho câu hỏi của bạn. Không có cách nào có thể dự đoán được khi nào một truy vấn có thể nghiêng về cách tiếp cận này hay cách khác - chỉ cần biết rằng, về mặt lý thuyết, cùng một ngữ nghĩa cho một CTE hoặc một truy vấn con sẽ thực hiện chính xác như nhau. Tôi nghĩ rằng câu hỏi của bạn sẽ có giá trị hơn nếu bạn trình bày một số trường hợp không đúng - có thể là bạn đã phát hiện ra một giới hạn trong trình tối ưu hóa (hoặc đã phát hiện ra một câu hỏi đã biết) hoặc có thể là các truy vấn của bạn không tương đương về mặt ngữ nghĩa hoặc cái đó chứa một yếu tố cản trở tối ưu hóa.

Vì vậy, tôi khuyên bạn nên viết truy vấn theo cách có vẻ tự nhiên nhất đối với bạn và chỉ đi chệch hướng khi bạn phát hiện ra một vấn đề hiệu suất thực tế mà trình tối ưu hóa đang gặp phải. Cá nhân tôi xếp hạng họ CTE, sau đó truy vấn con, với bảng #temp là giải pháp cuối cùng.


4
+1 hóa ra là một câu hỏi khá chủ quan; Tôi hy vọng nó không bị đóng cửa vì quá mơ hồ vì các câu trả lời cho đến nay vẫn có nhiều thông tin. Tôi nhận ra :-) bạn không thích nó khi câu hỏi thay đổi nhưng bạn có gợi ý nào để thu hẹp câu hỏi trong OP không?
whytheq

2
Tôi nghĩ rằng câu hỏi này là tốt, bạn sẽ nhận thấy chưa có một phiếu bầu nào để đóng, nhưng nếu các câu trả lời bắt đầu lung tung một cách điên cuồng thì có lẽ nó sẽ bị tắt. Như tôi đã đề xuất trong câu trả lời của mình, nếu bạn có một trường hợp cụ thể mà bạn thấy sự khác biệt lớn giữa CTE và truy vấn con, hãy bắt đầu một câu hỏi mới với các truy vấn thực tế và kế hoạch thực hiện (và nó có thể phù hợp hơn với dba.se ) . Chỉ cần nhận ra rằng câu trả lời để trợ giúp với truy vấn đó có thể không phải là cùng một câu trả lời cho một truy vấn khác có cùng kịch bản.
Aaron Bertrand

Ngay dưới câu hỏi của bạn có các liên kết link / edit / close / flag- nếu đã có bất kỳ phiếu bầu nào để đóng câu hỏi, bạn sẽ thấy close (n)nơi nđại diện cho số lượng người dùng đã bỏ phiếu để đóng câu hỏi của bạn. Nếu bạn nhấp vào liên kết, bạn sẽ thấy lý do những người dùng đó đã chọn.
Aaron Bertrand

@whytheq cũng xem bài đăng blog gần đây của Bob Beauchemin . Nó không xử lý CTE so với truy vấn con một cách cụ thể nhưng áp dụng cùng một loại khái niệm: nếu bạn chọn một mô hình không trực quan vì lý do hiệu suất, hãy ghi lại những điều tào lao ra khỏi nó và truy cập lại để đảm bảo rằng việc giải quyết bạn phát hiện ra là có thật. Tôi thậm chí có thể đề nghị để lại phiên bản tự nhiên hơn của truy vấn được nhận xét, trừ khi bạn có một hệ thống kiểm soát nguồn đáng tin cậy giữ phiên bản trước đó.
Aaron Bertrand

1
Đã sửa lỗi liên kết ở trên: sqlskills.com/bloss/bobb/ trên
ADJenks

19

#temp được mizedized và CTE thì không.

CTE chỉ là cú pháp nên về lý thuyết nó chỉ là một truy vấn con. Nó được thực thi. #temp được cụ thể hóa. Vì vậy, một CTE đắt tiền trong một phép nối được thực thi nhiều lần có thể tốt hơn trong #temp. Mặt khác, nếu đó là một đánh giá dễ dàng không được thực hiện nhưng một vài lần sau đó không xứng đáng với chi phí chung của #temp.

Có một số người trên SO không thích biến bảng nhưng tôi thích họ vì chúng được vật chất hóa và tạo nhanh hơn #temp. Đôi khi trình tối ưu hóa truy vấn làm tốt hơn với #temp so với biến bảng.

Khả năng tạo PK trên #temp hoặc biến bảng cung cấp cho trình tối ưu hóa truy vấn nhiều thông tin hơn CTE (vì bạn không thể khai báo PK trên CTE).


từ viết tắt "TVP" là gì ... tương tự như #temp?
whytheq

TVP đang trở thành một thuật ngữ phổ biến, bởi vì nó nghe có vẻ ấn tượng (với một số). Nói tóm lại, TVP là một bảng được truyền dưới dạng tham số. Bất cứ ai đã sử dụng biến Bảng sẽ ở ngay với họ.
WonderWorker

1
CẢNH BÁO - TVP không có kế hoạch thực hiện! Không sử dụng TVP cho bất cứ điều gì khác ngoài danh sách tra cứu ngắn đơn giản nhất. Nếu bạn thực hiện bất kỳ phép nối, chèn hoặc cập nhật phức tạp nào trên chúng, bạn có thể gặp phải các vấn đề tối ưu hóa lớn. Tin tôi đi, tôi đã bị cháy vì điều này.
Heliac

12

Chỉ có 2 điều tôi nghĩ làm cho nó LUÔN LUÔN nên sử dụng Bảng # Temp thay vì CTE là:

  1. Bạn không thể đặt khóa chính trên CTE để dữ liệu được CTE truy cập sẽ phải duyệt qua từng chỉ mục trong các bảng của CTE thay vì chỉ truy cập PK hoặc Index trên bảng tạm thời.

  2. Vì bạn không thể thêm các ràng buộc, chỉ mục và khóa chính vào CTE nên chúng dễ bị lỗi xâm nhập và dữ liệu xấu.


ngày hôm qua

Dưới đây là một ví dụ trong đó các ràng buộc #table có thể ngăn chặn dữ liệu xấu không phải là trường hợp trong CTE

DECLARE @BadData TABLE ( 
                       ThisID int
                     , ThatID int );
INSERT INTO @BadData
       ( ThisID
       , ThatID
       ) 
VALUES
       ( 1, 1 ),
       ( 1, 2 ),
       ( 2, 2 ),
       ( 1, 1 );

IF OBJECT_ID('tempdb..#This') IS NOT NULL
    DROP TABLE #This;
CREATE TABLE #This ( 
             ThisID int NOT NULL
           , ThatID int NOT NULL
                        UNIQUE(ThisID, ThatID) );
INSERT INTO #This
SELECT * FROM @BadData;
WITH This_CTE
     AS (SELECT *
           FROM @BadData)
     SELECT *
       FROM This_CTE;

3
ALWAYSlà hơi quá xa nhưng cảm ơn câu trả lời. Về khả năng đọc, việc sử dụng CTE có thể là một điều tốt.
whytheq

3
Tôi không hiểu điểm thứ hai của bạn cả. Theo cách tôi thấy, truy vấn xác định CTE tương tự như các ràng buộc bạn sẽ đặt trên bảng tạm thời, lưu ý rằng cái trước có thể chứa các biến vị ngữ phức tạp tùy ý trong khi cái sau bị giới hạn hơn nhiều (ví dụ: CHECKràng buộc tham chiếu đến nhiều hàng / bảng là không cho phép). Bạn có thể đăng một ví dụ trong đó CTE thể hiện một lỗi mà bảng tạm thời không tương đương không?
onedaywhen ngày
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.