L'espressione della tabella comune alias CTE in SQL Server fornisce un set di risultati temporaneo in T-SQL. Puoi farvi riferimento all'interno di un'istruzione SQL Select, SQL Insert, SQL Delete o SQL Update.
L'opzione è disponibile a partire da SQL Server 2005 e consente agli sviluppatori di scrivere query lunghe e complesse che coinvolgono molti JOIN, aggregazione e filtraggio dei dati. In genere, gli sviluppatori utilizzano le sottoquery per la scrittura di codici T-SQL e SQL ServerSQL Server archivia questi CTE in memoria temporaneamente fino al termine dell'esecuzione della query. Una volta terminata la query, viene rimossa dalla memoria.
CTE in SQL Server:sintassi
WITH <common_table_expression> ([column names])
AS
(
<query_definition>
)
<operation>
- Utilizza un nome CTE per fare riferimento ad esso per eseguire le istruzioni Seleziona, Inserisci, Aggiorna, Elimina o Unisci.
- I nomi delle colonne sono separati da virgole. Dovrebbero corrispondere alle colonne definite nella definizione della query.
- La definizione della query implica le istruzioni select da una singola tabella o join tra più tabelle.
- Puoi fare riferimento al nome dell'espressione CTE per recuperare i risultati.
Ad esempio, la seguente query CTE di base utilizza le parti seguenti:
- Nome espressione tabella comune – SalesCustomerData
- Elenco colonne:[ID cliente],[Nome],[Cognome],[Nome azienda],[Indirizzo email],[Telefono]
- La definizione della query include un'istruzione select che ottiene i dati dalla tabella [SalesLT].[Customer]
- L'ultima parte utilizza l'istruzione select sull'espressione CTE e filtra i record utilizzando la clausola 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
In un altro esempio, calcoliamo le vendite totali medie dal CTE. La definizione della query include la clausola GROUP BY. Successivamente, utilizziamo la funzione AVG() per calcolare il valore medio.
WITH Salesdata ([SalesOrderID],[Total])
AS(
SELECT [SalesOrderID]
,count(*) AS total
FROM [SalesLT].[SalesOrderHeader]
GROUP BY [SalesOrderID]
)
SELECT avg(total) FROM salesdata
Puoi anche utilizzare CTE per inserire dati nella tabella SQL. La definizione della query CTE include i dati richiesti che puoi recuperare dalle tabelle esistenti utilizzando i join. Successivamente, interroga CTE per inserire i dati nella tabella di destinazione.
Qui utilizziamo l'istruzione SELECT INTO per creare una nuova tabella denominata [CTEest] dall'output dell'istruzione CTE select.
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
Puoi anche specificare una tabella esistente che corrisponda alle colonne con i dati inseriti.
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
È anche possibile aggiornare o eliminare i record nella tabella SQL utilizzando l'espressione di tabella comune. Le seguenti query utilizzano le istruzioni DELETE e UPDATE con CTE.
Aggiorna dichiarazione in CTE
WITH Salesdata ([SalesOrderID],[Freight])
AS(
SELECT [SalesOrderID]
,[Freight]
FROM [SalesLT].[SalesOrderHeader]
)
UPDATE SalesData SET [Freight]=100.00 WHERE [SalesOrderID]=71774
Go
Elimina dichiarazione in 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
CTE multiple
È possibile dichiarare più CTE nello script T-SQL e utilizzare le operazioni di join su di essi. Per il CTE multiplo, T-SQL usa una virgola come separatore.
Nella query seguente, abbiamo due CTE:
- Vendite CTES
- CTESSalesDescription
Successivamente, nell'istruzione select, recuperiamo i risultati utilizzando INNER JOIN su entrambi i 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]
Espressioni di tabelle comuni ricorsive
Il CTE ricorsivo viene eseguito in un ciclo procedurale ripetuto fino a quando la condizione non è soddisfatta. L'esempio T-SQL seguente utilizza un contatore ID e seleziona i record fino a quando la condizione WHERE non viene soddisfatta.
Declare @ID int =1;
;with RecursiveCTE as
(
SELECT @ID as ID
UNION ALL
SELECT ID+ 1
FROM RecursiveCTE
WHERE ID <5
)
SELECT * FROM RecursiveCTE
Un altro uso di CTE ricorsivo in SQL Server è la visualizzazione di dati gerarchici. Supponiamo di avere un dipendente tabella e contiene record per tutti i dipendenti, i loro reparti e gli ID dei loro manager.
--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);
Ora dobbiamo generare i dati della gerarchia dei dipendenti. Possiamo usare CTE ricorsivo con UNION ALL nell'istruzione 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;
Il CTE restituisce i dettagli a livello di dipendente come mostrato di seguito.
Punti importanti relativi alle espressioni comuni delle tabelle
- Non possiamo riutilizzare il CTE. Il suo ambito è limitato alle istruzioni esterne SELECT, INSERT, UPDATE o MERGE.
- Puoi usare più CTES; tuttavia, dovrebbero utilizzare gli operatori UNION ALL, UNION, INTERSECT o EXCERPT.
- Possiamo definire più definizioni di query CTE nel CTE non ricorsivo.
- Non possiamo utilizzare ORDER BY (senza TOP), INTO, OPTIONS clausola con suggerimenti per la query e FOR BROWSE nella definizione della query CTE.
Ad esempio, lo script seguente utilizza la clausola ORDER BY senza una clausola 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
Dà il seguente errore:
- Non possiamo creare un indice sul CTE.
- I nomi delle colonne definiti in CTE devono corrispondere alle colonne restituite nell'istruzione select.
Il CTE non ha la colonna [Telefono] nel codice seguente mentre l'istruzione select ne restituisce il valore. Pertanto, viene visualizzato il messaggio di errore evidenziato.
- Se sono presenti più istruzioni nello script T-SQL, l'istruzione precedente prima di CTE deve terminare utilizzando un punto e virgola.
Ad esempio, la prima istruzione select non include un punto e virgola. Pertanto, viene visualizzato un errore di sintassi errato nello script CTE.
Lo script funziona correttamente se terminiamo la prima istruzione select utilizzando l'operatore punto e virgola.
- Non possiamo utilizzare una colonna duplicata nell'istruzione select se non dichiariamo il nome della colonna esternamente.
Ad esempio, la seguente definizione CTE specifica la colonna duplicata [Telefono]. Restituisce un errore.
Tuttavia, se si definiscono colonne esterne, non verranno generati errori. È necessario quando è necessaria una singola colonna più volte nell'output per calcoli diversi.
Importante:le Common Table Expressions (CTE) non sostituiscono le tabelle temporanee o le variabili di tabella.
- Le tabelle Temp vengono create nel TempDB e possiamo definire vincoli di indice simili a una tabella normale. Non possiamo fare riferimento alla tabella temporanea più volte in una sessione
- Le variabili di tabella esistono anche nel TempDB e agiscono come variabili che esistono durante l'esecuzione batch. Non possiamo definire un indice sulle variabili della tabella.
- CTE è per un unico scopo di riferimento e non possiamo definire l'indice su di esso. Esiste in memoria e viene eliminato dopo che è stato effettuato il riferimento.
Conclusione
Le Common Table Expressions (CTE) consentono agli sviluppatori di scrivere codice pulito ed efficace. In generale, puoi utilizzare CTE dove non sono necessari riferimenti multipli come una tabella temporanea e abbiamo esplorato vari scenari per SELECT, INSERT, UPDATE, istruzioni DETELTE e CTE ricorsivi.
Con l'aiuto di strumenti moderni, come il componente aggiuntivo SQL Complete SSMS, la gestione delle CTE diventa ancora più semplice. Il componente aggiuntivo può suggerire CTE al volo, rendendo così le attività che coinvolgono CTE molto più semplici. Inoltre, fare riferimento alla documentazione Microsoft per maggiori dettagli sul CTE.