Якщо CTE визначений у запиті і ніколи не використовується, він видає звук?


Відповіді:


21

Здається, це не так, але це дійсно стосується лише вкладених CTE.

Створіть дві тимчасові таблиці:

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

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

Запит 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;

Запит 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;

Плани запитів:

Горіхи

Є накладні витрати, але непотрібна частина запиту усувається дуже рано (під час розбору в цьому випадку; етап спрощення в більш складних випадках), тому додаткова робота справді мінімальна і не сприяє потенційно дорогим витратам оптимізація.


28

Поставив +1 Еріку, але хотів додати дві речі (які в коментарі не спрацювали):

  1. Вам навіть не потрібно дивитися на плани виконання, щоб побачити, що вони ігноруються, коли не використовуються. Нижче наведено помилку "поділ на 0", але вона не повинна cte2бути вибрана з усіх:

    ;WITH cte1 AS
    (
      SELECT 1 AS [Bob]
    ),
    cte2 AS (
      SELECT 1 / 0 AS [Err]
      FROM cte1
    )
    SELECT *
    FROM   cte1;
  2. CTE можна ігнорувати, навіть якщо вони є єдиними CTE, і навіть якщо вони вибрані, якщо логічно все-таки всі рядки будуть виключені. Далі йде випадок, коли оптимізатор запитів заздалегідь знає, що жоден рядок не може бути повернутий із CTE, тому він навіть не намагається його виконати:

    ;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;

Що стосується продуктивності, невикористаний CTE аналізується та збирається (або принаймні складається у нижченаведеному випадку), тому його не ігнорують на 100%, але вартість мала б бути незначною і не варто турбуватися про це.

Під час розбору лише помилок немає:

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

Коли ви робите все лише після виконання, то виникає проблема:

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'.
*/

Хотілося б, щоб я міг відзначити кілька правильних відповідей, але Ерік побив тебе до розіграшу пістолета. :) Але ваша відповідь дуже інформативна і чудова, дякую!
JD

Що робити, якщо CTE переглядаються, а представлення вкладено більше 3 разів? Хіба немає моменту, коли оптимізатор відмовляється і запускає все?
Зікато

@ Zikato Я поняття не маю, але це чудове питання. Ви повинні мати можливість встановити тест без особливих зусиль, створивши подання, використовуючи фокус ділення на нуль, який я показав у перших двох прикладах. Будь ласка, дайте мені знати результати, тому що мені дуже цікаво зараз щодо цього сценарію :-).
Соломон Руцький

@SolomonRutzky Чесно кажучи, я це протестував, але це не було переконливим. Я створив подання з вашого прикладу cte і вклав його 5 разів, але оскільки це все постійне сканування і не дуже складне, оптимізатор впорався з ним добре. Я хотів би перевірити це в майбутньому і приховати його за більш складною логікою. Я дам вам знати.
Zikato

@Zikato Цікаво. Не впевнений, що було б вважати "складним", але так, мій приклад дуже спрощений. Коли ви говорите "вкладено це 5 разів", ти маєш на увазі в інших переглядах / програмах, які дзвонять один одному, і це було 5 глибин, або в підзапитах / CTE? Я думаю, що існує можливість того, що достатньо рівнів гніздування може пропустити його, але не через те, що на нього не посилаються, а замість того, що він не використовує більш високий рівень гнізда, а припускається для нижчих рівнів. Я бачив, де витівка NEWID()в застосуванні подання для використання в UDF може повернути одне і те ж значення з декількох викликів завдяки оптимізатору, який кешує його.
Соломон Руцький
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.