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

Guide pour CTE dans SQL Server

L'expression de table commune alias CTE dans SQL Server fournit un jeu de résultats temporaire dans T-SQL. Vous pouvez vous y référer dans une instruction SQL Select, SQL Insert, SQL Delete ou SQL Update.

L'option est disponible à partir de SQL Server 2005, aidant les développeurs à écrire des requêtes complexes et longues impliquant de nombreux JOIN, agrégation et filtrage de données. Habituellement, les développeurs utilisent des sous-requêtes pour écrire des codes T-SQL, et SQL Server stocke temporairement ces CTE en mémoire jusqu'à la fin de l'exécution de la requête. Une fois la requête terminée, elle est supprimée de la mémoire.

CTE dans SQL Server :syntaxe

WITH <common_table_expression> ([column names])
AS
(
   <query_definition>
)
<operation>
  • Il utilise un nom CTE pour s'y référer afin d'effectuer les instructions Select, Insert, Update, Delete ou Merge.
  • Les noms de colonne sont séparés par des virgules. Elles doivent correspondre aux colonnes définies dans la définition de la requête.
  • La définition de la requête implique les instructions de sélection d'une seule table ou des jointures entre plusieurs tables.
  • Vous pouvez vous référer au nom de l'expression CTE pour récupérer les résultats.

Par exemple, la requête CTE de base suivante utilise les parties suivantes :

  • Nom de l'expression de table commune :SalesCustomerData
  • Liste des colonnes :[CustomerID],[FirstName],[LastName],[CompanyName],[EmailAddress],[Phone]
  • La définition de la requête inclut une instruction select qui obtient les données de la table [SalesLT].[Customer]
  • La dernière partie utilise l'instruction select sur l'expression CTE et filtre les enregistrements à l'aide de la clause where.
WITH SalesCustomerdata ([CustomerID],[FirstName],[LastName],[CompanyName],[EmailAddress],[Phone])
AS(
SELECT [CustomerID]
      ,[FirstName]
      ,[LastName]
      ,[CompanyName]
      ,[EmailAddress]
      ,[Phone]
   FROM [SalesLT].[Customer] 
)
SELECT * FROM SalesCustomerdata where Firstname like 'Raj%' 
ORDER BY CustomerID desc

Dans un autre exemple, nous calculons les ventes totales moyennes à partir du CTE. La définition de la requête inclut la clause GROUP BY. Plus tard, nous utilisons la fonction AVG() pour calculer la valeur moyenne.

WITH Salesdata ([SalesOrderID],[Total])
AS(
SELECT [SalesOrderID]
         ,count(*) AS total
          FROM [SalesLT].[SalesOrderHeader]
        GROUP BY [SalesOrderID]
)
SELECT avg(total) FROM salesdata

Vous pouvez également utiliser CTE pour insérer des données dans la table SQL. La définition de requête CTE inclut les données requises que vous pouvez extraire des tables existantes à l'aide de jointures. Plus tard, interrogez CTE pour insérer des données dans la table cible.

Ici, nous utilisons l'instruction SELECT INTO pour créer une nouvelle table nommée [CTETest] à partir de la sortie de l'instruction de sélection CTE.

WITH CTEDataInsert
AS 
(
SELECT
    p.[ProductID]
    ,p.[Name]
    ,pm.[Name] AS [ProductModel]
    ,pmx.[Culture]
    ,pd.[Description]
FROM [SalesLT].[Product] p
    INNER JOIN [SalesLT].[ProductModel] pm
    ON p.[ProductModelID] = pm.[ProductModelID]
    INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
    ON pm.[ProductModelID] = pmx.[ProductModelID]
    INNER JOIN [SalesLT].[ProductDescription] pd
    ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
)
SELECT * INTO CTETest FROM CTEDataInsert
GO

Vous pouvez également spécifier une table existante qui correspond aux colonnes avec les données insérées.

WITH CTEDataInsert
AS 
(
SELECT
    p.[ProductID]
    ,p.[Name]
    ,pm.[Name] AS [ProductModel]
    ,pmx.[Culture]
    ,pd.[Description]
FROM [SalesLT].[Product] p
    INNER JOIN [SalesLT].[ProductModel] pm
    ON p.[ProductModelID] = pm.[ProductModelID]
    INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
    ON pm.[ProductModelID] = pmx.[ProductModelID]
    INNER JOIN [SalesLT].[ProductDescription] pd
    ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
)
INSERT into CTETest select * FROM CTEDataInsert
GO

Vous pouvez également mettre à jour ou supprimer des enregistrements dans la table SQL à l'aide de l'expression de table commune. Les requêtes suivantes utilisent les instructions DELETE et UPDATE avec CTE.

Mettre à jour la déclaration dans CTE

WITH Salesdata ([SalesOrderID],[Freight])
AS(
SELECT [SalesOrderID]
         ,[Freight]
          FROM [SalesLT].[SalesOrderHeader]
        )
UPDATE SalesData SET [Freight]=100.00 WHERE [SalesOrderID]=71774
Go

Supprimer la déclaration dans CTE

WITH Salesdata ([SalesOrderID],[Freight])
AS(
SELECT [SalesOrderID]
         ,[Freight]
          FROM [SalesLT].[SalesOrderHeader]
        )
delete SalesData  WHERE [SalesOrderID]=71774
GO
SELECT * FROM [SalesLT].[SalesOrderHeader] WHERE SalesOrderID=71774

Plusieurs CTE

Vous pouvez déclarer plusieurs CTE dans le script T-SQL et utiliser les opérations de jointure sur eux. Pour les CTE multiples, T-SQL utilise une virgule comme séparateur.

Dans la requête suivante, nous avons deux CTE :

  1. CTESales
  2. CTESalesDescription

Plus tard, dans l'instruction select, nous récupérons les résultats en utilisant INNER JOIN sur les deux CTE.

WITH CTESales
AS 
(
SELECT
     p.[ProductID]
    ,p.[Name]
    ,pm.[Name] AS [ProductModel]
    ,pmx.[Culture]
    ,pmx.[ProductDescriptionID]
   FROM [SalesLT].[Product] p
    INNER JOIN [SalesLT].[ProductModel] pm
    ON p.[ProductModelID] = pm.[ProductModelID]
    INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
    ON pm.[ProductModelID] = pmx.[ProductModelID]
    INNER JOIN [SalesLT].[ProductDescription] pd
    ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
),CTESalesDescription

AS (

SELECT  description AS describe,[ProductDescriptionID]
from [SalesLT].[ProductDescription]  
)

SELECT  productid, [Name],[ProductModel],describe
FROM CTESales 
INNER JOIN CTESalesDescription 
    ON 
CTESales.[ProductDescriptionID] = CTESalesDescription.[ProductDescriptionID]

Expressions de table communes récursives

Le CTE récursif s'exécute dans une boucle procédurale répétée jusqu'à ce que la condition soit satisfaite. L'exemple T-SQL suivant utilise un compteur d'ID et sélectionne les enregistrements jusqu'à ce que la condition WHERE soit satisfaite.

Declare @ID int =1;
;with RecursiveCTE as  
   (  
      SELECT @ID as ID
        UNION ALL  
      SELECT  ID+ 1
  FROM  RecursiveCTE  
  WHERE ID <5
    )  
 
SELECT * FROM RecursiveCTE

Une autre utilisation du CTE récursif dans SQL Server consiste à afficher des données hiérarchiques. Supposons que nous ayons un employé table, et il contient des enregistrements pour tous les employés, leurs services et les identifiants de leurs responsables.

--Script Reference: Microsoft Docs

CREATE TABLE dbo.MyEmployees  
(  
EmployeeID SMALLINT NOT NULL,  
FirstName NVARCHAR(30)  NOT NULL,  
LastName  NVARCHAR(40) NOT NULL,  
Title NVARCHAR(50) NOT NULL,  
DeptID SMALLINT NOT NULL,  
ManagerID INT NULL,  
 CONSTRAINT PK_EmployeeID PRIMARY KEY CLUSTERED (EmployeeID ASC)   
);  
INSERT INTO dbo.MyEmployees VALUES   
 (1, N'Ken', N'Sánchez', N'Chief Executive Officer',16,NULL)  
,(273, N'Brian', N'Welcker', N'Vice President of Sales',3,1)  
,(274, N'Stephen', N'Jiang', N'North American Sales Manager',3,273)  
,(275, N'Michael', N'Blythe', N'Sales Representative',3,274)  
,(276, N'Linda', N'Mitchell', N'Sales Representative',3,274)  
,(285, N'Syed', N'Abbas', N'Pacific Sales Manager',3,273)  
,(286, N'Lynn', N'Tsoflias', N'Sales Representative',3,285)  
,(16,  N'David',N'Bradley', N'Marketing Manager', 4, 273)  
,(23,  N'Mary', N'Gibson', N'Marketing Specialist', 4, 16);

Maintenant, nous devons générer les données de la hiérarchie des employés. Nous pouvons utiliser CTE récursif avec UNION ALL dans l'instruction select.

WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort)  
AS (SELECT CONVERT(VARCHAR(255), e.FirstName + ' ' + e.LastName),  
        e.Title,  
        e.EmployeeID,  
        1,  
        CONVERT(VARCHAR(255), e.FirstName + ' ' + e.LastName)  
    FROM dbo.MyEmployees AS e  
    WHERE e.ManagerID IS NULL  
    UNION ALL  
    SELECT CONVERT(VARCHAR(255), REPLICATE ('|    ' , EmployeeLevel) +  
        e.FirstName + ' ' + e.LastName),  
        e.Title,  
        e.EmployeeID,  
        EmployeeLevel + 1,  
        CONVERT (VARCHAR(255), RTRIM(Sort) + '|    ' + FirstName + ' ' +   
                 LastName)  
    FROM dbo.MyEmployees AS e  
    JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID  
    )  
SELECT EmployeeID, Name, Title, EmployeeLevel  
FROM DirectReports   
ORDER BY Sort;

Le CTE renvoie les détails au niveau de l'employé comme indiqué ci-dessous.

Points importants concernant les expressions de table courantes

  • Nous ne pouvons pas réutiliser le CTE. Sa portée est limitée aux instructions externes SELECT, INSERT, UPDATE ou MERGE.
  • Vous pouvez utiliser plusieurs CTES ; cependant, ils doivent utiliser les opérateurs UNION ALL, UNION, INTERSECT ou EXCERPT.
  • Nous pouvons définir plusieurs définitions de requête CTE dans le CTE non récursif.
  • Nous ne pouvons pas utiliser la clause ORDER BY (sans TOP), INTO, OPTIONS avec des conseils de requête et FOR BROWSE dans la définition de requête CTE.

Par exemple, le script ci-dessous utilise la clause ORDER BY sans clause TOP.

WITH CTEDataInsert
AS 
(
SELECT
    p.[ProductID]
    ,p.[Name]
    ,pm.[Name] AS [ProductModel]
    ,pmx.[Culture]
    ,pd.[Description]
FROM [SalesLT].[Product] p
    INNER JOIN [SalesLT].[ProductModel] pm
    ON p.[ProductModelID] = pm.[ProductModelID]
    INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
    ON pm.[ProductModelID] = pmx.[ProductModelID]
    INNER JOIN [SalesLT].[ProductDescription] pd
    ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
    ORDER BY productid
)
select * FROM CTEDataInsert 
GO

Cela donne l'erreur suivante :

  • Nous ne pouvons pas créer d'index sur le CTE.
  • Les noms de colonne définis dans CTE doivent correspondre aux colonnes renvoyées dans l'instruction select.

Le CTE n'a pas la colonne [Téléphone] dans le code ci-dessous alors que l'instruction select renvoie sa valeur. Par conséquent, vous obtenez le message d'erreur en surbrillance.

  • Si vous avez plusieurs instructions dans le script T-SQL, l'instruction précédente avant CTE doit se terminer par un point-virgule.

Par exemple, la première instruction select n'inclut pas de point-virgule. Par conséquent, vous obtenez une erreur de syntaxe incorrecte dans le script CTE.

Le script fonctionne correctement si nous terminons la première instruction select à l'aide de l'opérateur point-virgule.

  • Nous ne pouvons pas utiliser une colonne en double dans l'instruction select si nous ne déclarons pas le nom de la colonne en externe.

Par exemple, la définition CTE suivante spécifie la colonne en double [Téléphone]. Il renvoie une erreur.

Cependant, si vous définissez des colonnes externes, cela ne provoquera pas d'erreurs. Il est requis lorsque vous avez besoin d'une seule colonne plusieurs fois dans la sortie pour différents calculs.

Important :Les expressions de table communes (CTE) ne remplacent pas les tables temporaires ou les variables de table.

  • Les tables temporaires sont créées dans TempDB, et nous pouvons définir des contraintes d'index similaires à une table normale. Nous ne pouvons pas référencer la table temporaire plusieurs fois dans une session
  • Les variables de table existent également dans TempDB et agissent comme des variables qui existent lors de l'exécution du lot. Nous ne pouvons pas définir d'index sur les variables de la table.
  • CTE est à des fins de référence unique, et nous ne pouvons pas définir l'index dessus. Il existe en mémoire et est déposé après référence.

Conclusion

Les expressions de table communes (CTE) permettent aux développeurs d'écrire du code propre et efficace. En général, vous pouvez utiliser CTE lorsque vous n'avez pas besoin de plusieurs références comme une table temporaire, et nous avons exploré divers scénarios pour SELECT, INSERT, UPDATE, l'instruction DETELTE et les CTE récursifs.

Avec l'aide d'outils modernes, tels que le complément SQL Complete SSMS, la gestion des CTE devient encore plus facile. Le complément peut suggérer des CTE à la volée, ce qui rend les tâches impliquant des CTE beaucoup plus simples. Reportez-vous également à la documentation Microsoft pour plus de détails sur le CTE.