Câu trả lời:
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:
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.
+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):
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 cte2
khô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;
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'.
*/
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.