web-dev-qa-db-fra.com

Si un CTE est défini dans une requête et n'est jamais utilisé, est-ce qu'il émet un son?

Les CTE inutilisés dans les requêtes affectent-ils les performances et/ou modifient-ils le plan de requête généré?

32
J.D.

Il ne semble pas qu'ils le fassent, mais cela ne s'applique vraiment qu'aux CTE imbriqués.

Créez deux tables temporaires:

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

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

Requête 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;

Requête 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;

Plans de requête:

NUTS

Il y a un surcoût, mais la partie inutile de la requête est éliminée très tôt (lors de l'analyse dans ce cas; l'étape de simplification dans les cas plus complexes), donc le travail supplémentaire est vraiment minime et ne contribue pas à des coûts potentiellement coûteux. optimisation.

21
Erik Darling

+1 à Erik, mais voulait ajouter deux choses (ce qui n'a pas bien fonctionné dans un commentaire):

  1. Vous n'avez même pas besoin de regarder les plans d'exécution pour voir qu'ils sont ignorés lorsqu'ils ne sont pas utilisés. Ce qui suit devrait produire une erreur "diviser par 0" mais ne résulte pas de cte2 pas du tout sélectionné:

    ;WITH cte1 AS
    (
      SELECT 1 AS [Bob]
    ),
    cte2 AS (
      SELECT 1 / 0 AS [Err]
      FROM cte1
    )
    SELECT *
    FROM   cte1;
    
  2. Les CTE peuvent être ignorés, même s'ils sont les seuls CTE, et même s'ils sont sélectionnés dans, if logiquement, toutes les lignes seraient de toute façon exclues. Voici un cas où l'optimiseur de requêtes sait à l'avance qu'aucune ligne ne peut être renvoyée par le CTE, il n'a donc même pas la peine de l'exécuter:

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

En ce qui concerne les performances, le CTE inutilisé est analysé et compilé (ou du moins compilé dans le cas ci-dessous), il n'est donc pas ignoré à 100%, mais le coût devrait être négligeable et ne devrait pas être concerné.

Lors de l'analyse, il n'y a pas d'erreur:

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

Lorsque vous faites tout juste avant l'exécution, il y a un problème:

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