Czy nieużywane CTE w zapytaniach wpływają na wydajność i / lub zmieniają wygenerowany plan zapytań?
Czy nieużywane CTE w zapytaniach wpływają na wydajność i / lub zmieniają wygenerowany plan zapytań?
Nie wydaje się, że tak, ale tak naprawdę dotyczy to tylko zagnieżdżonych CTE.
Utwórz dwie tabele tymczasowe:
CREATE TABLE #t1 (id INT);
INSERT #t1 ( id )
VALUES ( 1 );
CREATE TABLE #t2 (id INT);
INSERT #t2 ( id )
VALUES ( 1 );
Zapytanie 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;
Zapytanie 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;
Plany zapytań:
Istnieje narzut, ale niepotrzebna część zapytania jest eliminowana bardzo wcześnie (podczas analizy w tym przypadku; etap uproszczenia w bardziej skomplikowanych przypadkach), więc dodatkowa praca jest naprawdę minimalna i nie przyczynia się do potencjalnie kosztownych kosztów optymalizacja.
+1 do Erika, ale chciał dodać dwie rzeczy (które nie działały dobrze w komentarzu):
Nie musisz nawet patrzeć na plany wykonania, aby zobaczyć, że są one ignorowane, gdy nie są używane. Poniższe powinno powodować błąd „dziel przez 0”, ale nie wynika to z cte2
braku wyboru:
;WITH cte1 AS
(
SELECT 1 AS [Bob]
),
cte2 AS (
SELECT 1 / 0 AS [Err]
FROM cte1
)
SELECT *
FROM cte1;
CTE można zignorować, nawet jeśli są one jedynymi CTE, a nawet jeśli zostaną wybrane, jeśli logicznie i tak wszystkie wiersze zostaną wykluczone. Poniżej przedstawiono przypadek, w którym optymalizator zapytań wie z wyprzedzeniem, że żadne wiersze nie mogą zostać zwrócone z CTE, więc nawet nie zadaje sobie trudu, aby go wykonać:
;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;
Jeśli chodzi o wydajność, nieużywane CTE jest analizowane i kompilowane (lub przynajmniej kompilowane w poniższym przypadku), więc nie jest w 100% ignorowane, ale koszt musiałby być znikomy i nie warto się tym przejmować.
Podczas analizowania nie występuje błąd:
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
Gdy robisz wszystko tuż przed wykonaniem, pojawia się problem:
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()
w widoku do użycia w UDF może zwrócić tę samą wartość z wielu wywołań ze względu na buforowanie go przez optymalizator.