Database
 sql >> Base de données >  >> RDS >> Database

Tout ce que vous devez savoir sur SQL CTE en un seul endroit

La première fois que Karl a entendu parler de SQL Server CTE, c'était lorsqu'il cherchait quelque chose pour rendre son code SQL plus agréable à l'œil. C'est une sorte de mal de tête quand on le regarde. Anton, son collègue inquiet, lui a posé des questions sur CTE. Karl pensait qu'Anton faisait référence à son mal de tête. Peut-être qu'il a tout mal entendu, alors il a répondu:"Bien sûr que non." Le plus drôle, c'est qu'il faisait référence à l'encéphalopathie traumatique chronique, également une CTE - une maladie neurodégénérative causée par des traumatismes crâniens répétés. Mais d'après la réponse de Karl, Anton savait avec certitude que son collègue n'avait aucune idée de ce qu'il disait.

Quelle façon folle d'introduire les CTE ! Donc, avant d'embarquer dans le même bateau, clarifions, qu'est-ce que SQL CTE ou Common Table Expressions dans le monde SQL ?

Vous pouvez lire les bases ici. En attendant, nous en apprendrons un peu plus sur ce qui s'est passé dans cette histoire inhabituelle.

4 éléments de base sur CTE dans SQL Server

"Un CTE SQL a un nom"

Anton a commencé avec l'idée que les CTE SQL sont des ensembles de résultats temporairement nommés. Étant un moyen temporaire, le CTE a une portée limitée.

« Alors, c'est comme une sous-requête ? » Karl a demandé.

« D'une certaine manière, oui. Mais vous ne pouvez pas nommer une sous-requête », a déclaré Anton. "Un CTE a un nom un peu comme une table avec un nom. Cependant, au lieu de CREATE, vous utilisez WITH pour le créer. Puis, il a écrit la syntaxe sur papier :

WITH <cte_name>(<column list>)
AS
(
<inner query defining the CTE>
)
<outer query against CTE>

"CTE est parti lorsque le SELECT est terminé"

Anton a poursuivi en expliquant la portée de SQL CTE.

« Une table temporaire peut exister dans le périmètre de la procédure ou globalement. Mais CTE est parti lorsque le SELECT est terminé », a-t-il dit avec une rime. "Même chose si vous l'utilisez pour INSERT, UPDATE ou DELETE", a-t-il poursuivi.

"Vous ne pouvez pas le réutiliser"

"Contrairement à une vue ou à une table temporaire, vous ne pouvez pas réutiliser SQL CTE. Le nom est là, vous pouvez donc vous y référer dans la requête interne et externe. Mais c'est tout », a déclaré Anton.

« Alors, quel est le problème avec les CTE SQL ? » Karl a demandé.

"Vous pouvez rendre votre code plus lisible"

"La grosse affaire?" Anton a retourné la question. "C'est que vous pouvez rendre votre code facilement lisible. N'est-ce pas ce que vous cherchez ?"

"C'est vrai", a admis Karl.

Alors, quelle est la prochaine étape logique pour Karl ?

Informations supplémentaires sur CTE en SQL

Le lendemain, Karl a poursuivi sa quête de SQL CTE. En plus de ce qui précède, voici ce qu'il a trouvé :

  • SQL CTE peut être non récursif ou récursif.
  • Non seulement SQL Server, mais également MySQL et Oracle soutiennent cette idée. C'est, en fait, une partie des spécifications SQL-99.
  • Bien qu'il soit utilisé pour simplifier le code SQL, il n'améliore pas les performances.
  • Et cela ne remplacera pas non plus les sous-requêtes et les tables temporaires. Chacun a sa place et son utilisation.

En bref, c'est une autre façon d'exprimer une requête .

Mais Karl avait soif de plus de détails, alors il a continué à chercher ce qui fonctionnerait, ce qui ne fonctionnerait pas, et comment cela fonctionnerait par rapport aux sous-requêtes et aux tables temporaires.

Qu'est-ce qui fonctionnera dans SQL Server CTE ?

Pour en savoir plus sur CTE, Karl a énuméré ci-dessous ce que SQL Server accepterait. Jetez également un coup d'œil à ses études.

Attribuer des alias de colonne intégrés ou externes

Les CTE SQL prennent en charge deux formes d'attribution d'alias de colonne. Le premier est le formulaire en ligne, comme dans l'exemple ci-dessous :

-- Use an Inline column alias

USE AdventureWorks
GO;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,a.NumberOfOrders
FROM Sales_CTE a

Le code ci-dessus utilise un alias de colonne dans la définition CTE lorsqu'il est affecté dans l'instruction SELECT. Avez-vous remarqué le COUNT(*) AS NumberOfOrders ? C'est le formulaire en ligne.

Maintenant, un autre exemple est le formulaire externe :

-- Use an external column alias

USE AdventureWorks
GO;

WITH Sales_CTE(SalesPersonID, NumberOfOrders) 
AS  
(  
	SELECT SalesPersonID, COUNT(*)
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,a.NumberOfOrders
FROM Sales_CTE a

Les colonnes peuvent également être définies entre parenthèses après avoir défini le nom CTE. Remarquez le WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE en SQL précède un SELECT, INSERT, UPDATE ou DELETE

Cet article suivant concerne la consommation du CTE. Le premier exemple courant est lorsqu'il précède une instruction SELECT.

-- List down all Salespersons with their all-time number of orders
USE AdventureWorks
GO;

WITH Sales_CTE (SalesPersonID, NumberOfOrders)  
AS  
(  
	SELECT SalesPersonID, COUNT(*)  
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,CONCAT(P.LastName,', ',P.FirstName,' ',P.MiddleName) AS SalesPerson
,a.NumberOfOrders
FROM Sales_CTE a
INNER JOIN Person.Person p ON a.SalesPersonID = p.BusinessEntityID

Que montre cet exemple ?

  • Sales_CTE – le nom du CTE.
  • (SalesPersonID, NumberOfOrders) – la définition des colonnes CTE.
  • SELECT SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader WHERE SalesPersonID IS NOT NULL GROUP BY SalesPersonID – le SELECT interne qui définit le CTE.
  • SELECT a.SalesPersonID, CONCAT(P.LastName,’, ‘,P.FirstName,’ ‘,P.MiddleName) AS SalesPerson – la requête externe qui consomme le CTE. Cet exemple utilise un SELECT pour consommer le CTE.
  • DE Sales_CTE a – la référence de la requête externe au CTE.

Outre un SELECT, il fonctionne également avec INSERT, UPDATE et DELETE. Voici un exemple d'utilisation de INSERT :

-- add a 10% increase to Employee 16 after 1 year from the previous increase.
USE AdventureWorks
GO;

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    ,eph.PayFrequency
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
)
INSERT INTO HumanResources.EmployeePayHistory
SELECT
 BusinessEntityID
,DATEADD(d,365,RateChangeDate)
,(Rate * 0.1) + Rate
,PayFrequency
,GETDATE()
FROM LatestEmployeePay

Dans la liste ci-dessus, le CTE récupère le dernier salaire de l'employé 16. Le jeu de résultats du CTE est ensuite utilisé pour insérer un nouvel enregistrement dans EmployeePayHistory . Karl a enregistré ses découvertes avec élégance. En outre, il a utilisé des exemples appropriés.

Définir plusieurs CTE en une seule requête

C'est exact. Karl a constaté que plusieurs CTE sont possibles dans 1 requête. Voici un exemple :

-- Get the present and previous rate of employee 16
USE AdventureWorks
GO;

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
),
PreviousEmployeePay AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph
    INNER JOIN LatestEmployeePay lep 
      ON eph.BusinessEntityID = lep.BusinessEntityID
    WHERE eph.BusinessEntityID = 16
      AND eph.RateChangeDate < lep.RateChangeDate
    ORDER BY eph.RateChangeDate DESC
)
SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM LatestEmployeePay a
INNER JOIN PreviousEmployeePay b 
    ON a.BusinessEntityID = b.BusinessEntityID

Le code ci-dessus utilise 2 CTE dans une requête, à savoir LatestEmployeePay et PreviousEmployeePay .

Référer à un CTE plusieurs fois

Il y a plus à l'exemple précédent. Notez également que vous pouvez INNER JOIN le premier CTE au deuxième CTE. Enfin, la requête externe peut joindre les deux CTE. Le LatestEmployeePay a été mentionné deux fois.

Transmettre les arguments à un CTE SQL

Des arguments, tels que des variables, peuvent être transmis le long d'un CTE :

DECLARE @SalesPersonID INT = 275;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

Le code ci-dessus commence par déclarer et définir une variable @SalesPersonID . La valeur est ensuite transmise au CTE pour filtrer le résultat.

Utiliser dans un CURSEUR

Un curseur SQL peut utiliser une instruction SELECT et parcourir les résultats. En outre, un CTE SQL peut être utilisé avec :

DECLARE @SalesPersonID INT
DECLARE @NumberofOrders INT

DECLARE sales_cursor CURSOR FOR
    WITH Sales_CTE (SalesPersonID, NumberOfOrders)  
	AS  
	(  
		SELECT SalesPersonID, COUNT(*)  
		FROM Sales.SalesOrderHeader  
		WHERE SalesPersonID IS NOT NULL  
		GROUP BY SalesPersonID  
	)  
	SELECT salespersonid, numberoforders
	FROM Sales_CTE; 
OPEN sales_cursor
FETCH NEXT FROM sales_cursor INTO @SalesPersonID, @NumberofOrders
WHILE @@FETCH_STATUS = 0  
BEGIN
	PRINT 'SalesPersonID: ' + CAST(@SalesPersonID AS VARCHAR)
	PRINT '# of Orders: ' + CAST(@NumberofOrders AS VARCHAR)
	FETCH NEXT FROM sales_cursor  INTO @SalesPersonID, @NumberofOrders
END
CLOSE sales_cursor
DEALLOCATE sales_cursor;

Utiliser une table temporaire dans un CTE récursif

CTE récursif utilise un membre d'ancrage et un membre récursif dans la définition CTE. Cela aide à obtenir des hiérarchies dans une table. SQL CTE peut également utiliser une table temporaire à cette fin. Voir un exemple ci-dessous :

-- Create a Crew table.  
CREATE TABLE #EnterpriseDSeniorOfficers  
(  
CrewID SMALLINT NOT NULL,  
FirstName NVARCHAR(30)  NOT NULL,  
LastName  NVARCHAR(40) NOT NULL,  
CrewRank NVARCHAR(50) NOT NULL,  
HigherRankID INT NULL,  
 CONSTRAINT PK_CrewID PRIMARY KEY CLUSTERED (CrewID ASC)   
);  
-- Populate the table with values.  
INSERT INTO #EnterpriseDSeniorOfficers VALUES   
 (1, N'Jean-Luc', N'Picard', N'Captain',NULL)  
,(2, N'William', N'Riker', N'First Officer',1)  
,(3, N'Data', N'', N'Second Officer',1)  
,(4, N'Worf', N'', N'Chief of Security',1)  
,(5, N'Deanna', N'Troi', N'Ship Counselor',1)  
,(6, N'Beveryly', N'Crusher', N'Chief Medical Officer',1)  
,(7, N'Geordi', N'LaForge', N'Chief Engineer',1);  

WITH DirectReports(HigherRankID, CrewID, Title, CrewLevel) AS   
(  
    SELECT HigherRankID, CrewID, CrewRank, 0 as CrewLevel
    FROM #EnterpriseDSeniorOfficers
    WHERE HigherRankID IS NULL  
    UNION ALL  
    SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
    FROM #EnterpriseDSeniorOfficers AS e  
        INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID   
)  
SELECT HigherRankID, CrewID, Title, CrewLevel   
FROM DirectReports  
OPTION (MAXRECURSION 2)
ORDER BY HigherRankID;  

DROP TABLE #EnterpriseDSeniorOfficers

Karl a expliqué en disséquant ce CTE. Voici comment ça se passe.

Le membre d'ancrage est la première instruction SELECT avec le niveau d'équipage zéro (0) :

SELECT HigherRankID, CrewID, CrewRank, 0 as CrewLevel
 FROM #EnterpriseDSeniorOfficers
 WHERE HigherRankID IS NULL

Ce membre d'ancrage obtient le nœud racine de la hiérarchie. La clause WHERE spécifie que le niveau racine (HigherRankID IS NULL ).

Le membre récursif qui obtiendra les nœuds enfants est extrait ci-dessous :

SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
FROM #EnterpriseDSeniorOfficers AS e  
INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID

Il y a aussi une OPTION (MAXRECURSION 2) utilisé dans la requête externe. Les CTE récursifs peuvent devenir problématiques lorsqu'une boucle infinie résulte de la requête récursive. MAXRECURSION 2 évite ce gâchis - il limite la boucle à 2 récursions seulement.

Ceci termine la liste de Karl de ce qui fonctionnera. Cependant, tout ce à quoi nous pensons ne peut pas fonctionner. La section suivante discutera des découvertes de Karl à ce sujet.

Qu'est-ce qui ne fonctionnera pas dans SQL CTE ?

Ici, nous avons une liste de choses qui généreront une erreur lors de l'utilisation de SQL CTE.

Aucun point-virgule précédant le CTE SQL

S'il y a une déclaration avant le CTE, cette déclaration doit se terminer par un point-virgule. La clause WITH peut fonctionner à d'autres fins comme dans un indicateur de table, ainsi, le point-virgule éliminera l'ambiguïté. L'instruction fournie ci-dessous entraînera une erreur :

DECLARE @SalesPersonID INT

SET @SalesPersonID = 275

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

Les débutants qui ne terminaient pas les instructions par un point-virgule rencontrent cette erreur :

Colonnes sans nom

"Vous avez oublié de mettre un alias de colonne ? Ensuite, vous êtes dans une autre erreur. Karl l'a dit dans son article et a également fourni un exemple de code que je partage ci-dessous :

DECLARE @SalesPersonID INT

SET @SalesPersonID = 275;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*)
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

Ensuite, regardez le message d'erreur :

Noms de colonne en double

Une autre erreur liée à #2 ci-dessus utilise le même nom de colonne dans le CTE. Vous pouvez vous en tirer avec une instruction SELECT normale, mais pas avec un CTE. Karl avait un autre exemple :

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID AS col1, COUNT(*) AS col1
	FROM Sales.SalesOrderHeader 
	GROUP BY SalesPersonID  
)  
SELECT *
FROM Sales_CTE

Clause ORDER BY sans TOP ni OFFSET-FETCH

Le SQL standard n'autorise pas ORDER BY dans les expressions de table lorsque nous l'utilisons pour trier les ensembles de résultats. Cependant, si TOP ou OFFSET-FETCH est utilisé, ORDER BY devient une aide au filtrage.

Voici l'exemple de Karl utilisant un ORDER BY :

WITH LatestEmployeePay
AS
(
    SELECT
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    ,eph.PayFrequency
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
)
INSERT INTO HumanResources.EmployeePayHistory
SELECT
 BusinessEntityID
,DATEADD(d,365,RateChangeDate)
,(Rate * 0.1) + Rate
,PayFrequency
,GETDATE()
FROM LatestEmployeePay

Notez qu'il s'agit du même exemple que nous avions précédemment, mais cette fois, TOP n'est pas spécifié. Vérifiez l'erreur :

Le nombre de colonnes n'est pas le même que la définition de la liste de colonnes

Karl a utilisé le même exemple CTE récursif, mais il a supprimé une colonne dans le membre d'ancrage :

WITH DirectReports(HigherRankID, CrewID, Title, CrewLevel) AS   
(  
    SELECT HigherRankID, CrewID
    FROM #EnterpriseDSeniorOfficers
    WHERE HigherRankID IS NULL  
    UNION ALL  
    SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
    FROM #EnterpriseDSeniorOfficers AS e  
        INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID   
)  
SELECT HigherRankID, CrewID, Title, CrewLevel   
FROM DirectReports  
ORDER BY HigherRankID;

Le code ci-dessus utilise une liste à trois colonnes avec un formulaire externe, mais le membre d'ancrage n'avait que 2 colonnes. Ce n'est pas permis. Faire une erreur comme celle-ci déclenchera une erreur :

Autres éléments non autorisés dans un CTE

Outre la liste ci-dessus, voici quelques autres découvertes de Karl qui déclenchent des erreurs si vous les utilisez par erreur dans un CTE SQL.

  • Utilisation de SELECT INTO, de la clause OPTION avec des conseils de requête et de FOR BROWSE.
  • Données et types différents dans les colonnes de membres d'ancrage par rapport aux colonnes de membres récursifs.
  • Avoir les mots clés suivants dans un membre récursif d'un CTE récursif :
    • HAUT
    • OUTER JOIN (Mais INNER JOIN est autorisé)
    • GROUPER PAR et AVOIR
    • Sous-requêtes
    • SÉLECTIONNER DISTINCT
  • Utilisation de l'agrégation scalaire.
  • Utiliser des sous-requêtes dans un membre récursif.
  • Ayant des CTE SQL imbriqués.

SQL CTE, tables temporaires et sous-requêtes

Parfois, vous pouvez réécrire un CTE SQL à l'aide d'une sous-requête. De plus, vous pouvez parfois décomposer un CTE SQL à l'aide de tables temporaires pour des raisons de performances. Comme toute autre requête, vous devez vérifier le plan d'exécution réel et les E/S STATISTIQUES pour savoir quelle option choisir. SQL CTE peut être agréable à regarder, mais si vous rencontrez un mur de performances, utilisez une autre option . Aucune option n'est plus rapide que l'autre.

Examinons trois requêtes des articles de Karl qui apportent les mêmes résultats. L'un utilise un SQL CTE, l'autre utilise une sous-requête et le troisième utilise une table temporaire. Pour plus de simplicité, Karl a utilisé un petit ensemble de résultats.

Le code et le jeu de résultats

Il a commencé par utiliser plusieurs CTE SQL.

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
),
PreviousEmployeePay AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph
    INNER JOIN LatestEmployeePay lep 
        ON eph.BusinessEntityID = lep.BusinessEntityID
    WHERE eph.BusinessEntityID = 16
        AND eph.RateChangeDate < lep.RateChangeDate
    ORDER BY eph.RateChangeDate DESC
)
SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM LatestEmployeePay a
INNER JOIN PreviousEmployeePay b 
    ON a.BusinessEntityID = b.BusinessEntityID

La suivante est une sous-requête. Comme vous le remarquez, le CTE semble modulaire et lisible, mais la sous-requête ci-dessous est plus courte :

SELECT TOP 1
 eph.BusinessEntityID
,eph.Rate
,eph.RateChangeDate
,(SELECT TOP 1 eph1.Rate FROM HumanResources.EmployeePayHistory eph1
  WHERE eph1.BusinessEntityID=16
    AND eph1.RateChangeDate < eph.RateChangeDate
  ORDER BY eph1.RateChangeDate DESC) AS PreviousRate
FROM HumanResources.EmployeePayHistory eph
WHERE eph.BusinessEntityID = 16
ORDER BY eph.RateChangeDate DESC;

Karl a également essayé de le diviser en petits morceaux de code, puis de combiner les résultats à l'aide de tables temporaires.

SELECT TOP 1
 eph.BusinessEntityID
,eph.RateChangeDate
,eph.Rate
INTO #LatestPay
FROM HumanResources.EmployeePayHistory eph 
WHERE eph.BusinessEntityID = 16
ORDER BY eph.RateChangeDate DESC

SELECT TOP 1
 eph.BusinessEntityID
,eph.RateChangeDate
,eph.Rate
INTO #PreviousPay
FROM HumanResources.EmployeePayHistory eph
INNER JOIN #LatestPay lep 
    ON eph.BusinessEntityID = lep.BusinessEntityID
WHERE eph.BusinessEntityID = 16
    AND eph.RateChangeDate < lep.RateChangeDate
ORDER BY eph.RateChangeDate DESC

SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM #LatestPay a
INNER JOIN #PreviousPay b 
    ON a.BusinessEntityID = b.BusinessEntityID

DROP TABLE #LatestPay
DROP TABLE #PreviousPay

Jetez un œil à ces trois façons d'obtenir les résultats actuels et précédents de l'employé 16. Ils sont identiques :

Les lectures logiques

Qu'est-ce qui consomme le plus de ressources SQL Server ? Regardons l'ES STATISTIQUES. Karl a utilisé statisticsparser.com pour bien mettre en forme les résultats - tant mieux pour nous.

La figure 7 montre les lectures logiques de l'utilisation d'un CTE par rapport à l'utilisation d'une sous-requête :

Ensuite, consultez le nombre total de lectures logiques lorsque vous décomposez le code en petits morceaux à l'aide de tables temporaires :

Alors, qu'est-ce qui consomme le plus de ressources ? Karl les a classés pour plus de clarté.

  1. Sous-requête :4 lectures logiques (GAGNANT !).
  2. SQL CTE – 6 lectures logiques.
  3. Tables temporaires :8 lectures logiques.

Dans cet exemple, la plus rapide sera la sous-requête.

Le plan d'exécution réel

Pour donner un sens aux lectures logiques que nous avons obtenues de l'OI STATISTIQUES, Karl a également vérifié le plan d'exécution réel. Il est parti du CTE :

Nous pouvons observer quelques éléments à partir de ce plan :

  • LatestEmployeePay CTE a été évalué deux fois lorsqu'il est utilisé dans la requête externe et lorsqu'il est joint à PreviousEmployeePay . Donc, nous voyons 2 nœuds TOP pour cela.
  • Nous voyons PreviousEmployeePay évalué une fois.

Ensuite, regardez le plan d'exécution réel de la requête avec une sous-requête :

Il y a quelques choses évidentes ici :

  • Le plan est plus simple.
  • C'est plus simple car la sous-requête pour obtenir le dernier salaire n'est évaluée qu'une seule fois.
  • Pas étonnant que les lectures logiques soient inférieures par rapport aux lectures logiques de la requête avec un CTE.

Enfin, voici le plan d'exécution réel lorsque Karl a utilisé des tables temporaires :

Puisqu'il s'agit d'un lot de trois déclarations, nous voyons également trois diagrammes dans le plan. Tous les trois sont simples, mais le plan collectif n'est pas aussi simple que le plan de la requête avec la sous-requête.

Les statistiques de temps

À l'aide de la solution dbForge Studio for SQL Server, vous pouvez comparer les statistiques de temps dans Query Profiler. Pour cela, maintenez la touche CTRL enfoncée et cliquez sur les noms des résultats de chaque requête dans l'historique d'exécution :

Les statistiques de temps sont cohérentes avec les lectures logiques et le plan d'exécution réel. La sous-requête a été la plus rapide (88 ms). Vient ensuite le CTE (199 ms). Le dernier est l'utilisation de tables temporaires (536ms).

Alors, qu'avons-nous appris de Karl ?

Dans cet exemple particulier, nous avons vu que l'utilisation d'une sous-requête est bien meilleure lorsque nous voulons l'option la plus rapide. Cependant, cela peut être une autre histoire si l'ensemble des exigences n'est pas tel.

Vérifiez toujours les E/S STATISTIQUES et les plans d'exécution réels pour savoir quelle technique utiliser.

À emporter

J'espère que vous ne vous êtes pas cogné la tête contre le mur pour comprendre ce qu'est un CTE (Common Table Expressions). Sinon, vous pourriez avoir une CTE (encéphalopathie traumatique chronique). Blague à part, qu'avons-nous révélé ?

  • Les expressions de table communes sont des ensembles de résultats temporairement nommés. C'est plus proche d'une sous-requête en termes de comportement et de portée, mais plus clair et plus modulaire. Il a aussi un nom.
  • SQL CTE sert à simplifier le code, pas à accélérer votre requête.
  • Sept choses que nous avons apprises fonctionneront sur SQL CTE.
  • Cinq éléments déclencheront une erreur.
  • Nous avons également confirmé une fois de plus que les STATISTICS IO et le plan d'exécution réel vous donneront toujours un meilleur jugement. C'est vrai si vous comparez un CTE à une sous-requête et un lot à l'aide de tables temporaires.

Apprécié? Ensuite, les boutons des médias sociaux attendent d'être pressés. Choisissez votre préféré et partagez l'amour !

Lire aussi

Comment CTE peut aider à écrire des requêtes complexes et puissantes :une perspective de performance