Nếu một CTE được xác định trong một truy vấn và không bao giờ được sử dụng, nó có tạo ra âm thanh không?


32

Các CTE không được sử dụng trong các truy vấn có ảnh hưởng đến hiệu suất và / hoặc thay đổi gói truy vấn được tạo không?

Câu trả lời:


21

Có vẻ như họ không làm vậy, nhưng điều này thực sự chỉ áp dụng cho các CTE lồng nhau.

Tạo hai bảng tạm thời:

CREATE TABLE #t1 (id INT);
INSERT #t1 ( id )
VALUES ( 1 );

CREATE TABLE #t2 (id INT);
INSERT #t2 ( id )
VALUES ( 1 );

Truy vấn 1:

WITH your_mom AS (
    SELECT TOP 1 *
    FROM #t1 AS t 
),
also_your_mom AS (
    SELECT TOP 1 *
    FROM #t2 AS t
)
SELECT *
FROM your_mom;

Truy vấn 2:

WITH your_mom AS (
    SELECT TOP 1 *
    FROM #t1 AS t 
),
also_your_mom AS (
    SELECT TOP 1 *
    FROM #t2 AS t
)
SELECT *
FROM also_your_mom;

Kế hoạch truy vấn:

QUẢ HẠCH

Có một chi phí chung, nhưng phần không cần thiết của truy vấn bị loại bỏ rất sớm (trong quá trình phân tích cú pháp trong trường hợp này; giai đoạn đơn giản hóa trong các trường hợp phức tạp hơn), vì vậy công việc bổ sung thực sự tối thiểu và không đóng góp vào chi phí tiềm năng đắt đỏ tối ưu hóa.


28

+1 cho Erik, nhưng muốn thêm hai điều (không hoạt động tốt trong một nhận xét):

  1. Bạn thậm chí không cần nhìn vào các kế hoạch thực hiện để thấy rằng chúng bị bỏ qua khi không được sử dụng. Sau đây sẽ tạo ra lỗi "chia cho 0" nhưng không phải do cte2không được chọn từ tất cả:

    ;WITH cte1 AS
    (
      SELECT 1 AS [Bob]
    ),
    cte2 AS (
      SELECT 1 / 0 AS [Err]
      FROM cte1
    )
    SELECT *
    FROM   cte1;
  2. CTE có thể bị bỏ qua, ngay cả khi chúng là CTE duy nhất và ngay cả khi chúng được chọn từ đó, nếu theo logic thì tất cả các hàng sẽ bị loại trừ. Sau đây là trường hợp trình tối ưu hóa truy vấn biết trước rằng không có hàng nào có thể được trả về từ CTE, do đó, thậm chí không cần thực hiện nó:

    ;WITH cte AS
    (
      SELECT 1 / 0 AS [Bob]
    )
    SELECT TOP (1) [object_id]
    FROM   sys.objects
    UNION ALL
    SELECT cte.[Bob]
    FROM   cte
    WHERE  1 = 0;

Về hiệu suất, CTE không sử dụng được phân tích cú pháp và biên dịch (hoặc ít nhất là được biên dịch trong trường hợp bên dưới), vì vậy nó không bị bỏ qua 100%, nhưng chi phí sẽ không đáng kể và không đáng quan tâm.

Khi chỉ phân tích cú pháp, không có lỗi:

SET PARSEONLY ON;

;WITH cte1 AS
(
  SELECT obj.[NotHere]
  FROM   sys.objects obj
)
SELECT TOP (1) so.[name]
FROM   sys.objects so

GO
SET PARSEONLY OFF;
GO

Khi thực hiện mọi thứ chỉ cần thực hiện ngắn, thì có một vấn đề:

GO
SET NOEXEC ON;
GO

;WITH cte1 AS
(
  SELECT obj.[NotHere]
  FROM   sys.objects obj
)
SELECT TOP (1) so.[name]
FROM   sys.objects so

GO
SET NOEXEC OFF;
GO
/*
Msg 207, Level 16, State 1, Line XXXXX
Invalid column name 'NotHere'.
*/

Ước gì tôi có thể đánh dấu nhiều hơn một câu trả lời là đúng, nhưng Erik đã đánh bại bạn để rút súng lục. :) Nhưng câu trả lời của bạn rất nhiều thông tin và tuyệt vời, cảm ơn bạn!
JD

Điều gì xảy ra nếu CTE ở trong Chế độ xem và chế độ xem được lồng hơn 3 lần? Không có điểm nào tối ưu hóa từ bỏ và chạy tất cả?
Zikato

@Zikato Tôi không có ý kiến ​​gì, nhưng đó là một câu hỏi hay. Bạn sẽ có thể thiết lập một bài kiểm tra mà không cần quá nhiều nỗ lực bằng cách tạo chế độ xem bằng cách sử dụng phép chia cho số 0 mà tôi đã trình bày trong hai ví dụ đầu tiên. Xin vui lòng cho tôi biết kết quả vì bây giờ tôi rất tò mò về kịch bản này :-).
Solomon Rutzky

@SolomonRutzky Để công bằng, tôi đã thử nghiệm nó, nhưng nó không được kết luận. Tôi đã tạo một chế độ xem từ ví dụ cte của bạn và lồng nó 5 lần, nhưng vì tất cả đều quét liên tục và không thực sự phức tạp, trình tối ưu hóa đã xử lý tốt. Tôi muốn kiểm tra nó kỹ lưỡng hơn trong tương lai và ẩn nó đằng sau logic phức tạp hơn. Tôi sẽ cho bạn biết.
Zikato

@Zikato Thú vị. Không chắc chắn những gì sẽ được coi là "phức tạp", nhưng vâng, ví dụ của tôi rất đơn giản. Khi bạn nói "lồng nhau 5 lần", bạn có nghĩa là trong các chế độ xem / procs khác gọi nhau và nó sâu 5 lần, hoặc trong các truy vấn con / CTE? Tôi nghĩ rằng có khả năng các cấp độ lồng đủ có thể bỏ qua nó, nhưng không phải do nó không được tham chiếu mà thay vào đó do cấp độ tổ cao hơn không sử dụng nó và được giả định cho các cấp thấp hơn. Tôi đã thấy thủ thuật đưa NEWID()vào chế độ xem để sử dụng trong UDF có thể trả về cùng một giá trị từ nhiều cuộc gọi do trình tối ưu hóa lưu vào bộ đệm.
Solomon Rutzky
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.