Det vanliga tabelluttrycket aka CTE i SQL Server ger en tillfällig resultatuppsättning i T-SQL. Du kan referera till det i en SQL Select-, SQL Insert-, SQL Delete- eller SQL Update-sats.
Alternativet är tillgängligt från SQL Server 2005 och framåt, och hjälper utvecklarna att skriva komplexa och långa frågor som involverar många JOINs, aggregering och datafiltrering. Vanligtvis använder utvecklare underfrågor för att skriva T-SQL-koder, och SQL Server lagrar dessa CTE i minnet tillfälligt tills exekveringen av frågan är klar. När frågan är klar tas den bort från minnet.
CTE i SQL Server:Syntax
WITH <common_table_expression> ([column names])
AS
(
<query_definition>
)
<operation>
- Den använder ett CTE-namn för att hänvisa till den för att utföra satserna Select, Insert, Update, Delete eller Merge.
- Kolumnnamn är kommaseparerade. De bör matcha kolumnerna som definieras i frågedefinitionen.
- Frågedefinitionen involverar urvalssatserna från en enda tabell eller sammanfogar mellan flera tabeller.
- Du kan hänvisa till CTE-uttryckets namn för att hämta resultaten.
Till exempel använder följande grundläggande CTE-fråga följande delar:
- Common Table Expression name – SalesCustomerData
- Kolumnlista – [Kund-ID],[Förnamn],[Efternamn],[Företagsnamn],[E-postadress],[Telefon]
- Frågedefinitionen innehåller en select-sats som hämtar data från tabellen [SalesLT].[Customer]
- Den sista delen använder select-satsen på CTE-uttrycket och filtrerar poster med where-satsen.
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
I ett annat exempel beräknar vi den genomsnittliga totala försäljningen från CTE. Frågedefinitionen inkluderar GROUP BY-satsen. Senare använder vi funktionen AVG() för att beräkna medelvärdet.
WITH Salesdata ([SalesOrderID],[Total])
AS(
SELECT [SalesOrderID]
,count(*) AS total
FROM [SalesLT].[SalesOrderHeader]
GROUP BY [SalesOrderID]
)
SELECT avg(total) FROM salesdata
Du kan också använda CTE för att infoga data i SQL-tabellen. CTE-frågedefinitionen inkluderar de nödvändiga data som du kan hämta från befintliga tabeller med hjälp av joins. Senare, fråga CTE för att infoga data i måltabellen.
Här använder vi SELECT INTO-satsen för att skapa en ny tabell med namnet [CTETest] från utdata från CTE select-satsen.
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
Du kan också ange en befintlig tabell som matchar kolumnerna med infogade data.
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
Du kan också uppdatera eller ta bort poster i SQL-tabellen med det vanliga tabelluttrycket. Följande frågor använder DELETE- och UPDATE-satser med CTE.
Uppdatera uttalande i CTE
WITH Salesdata ([SalesOrderID],[Freight])
AS(
SELECT [SalesOrderID]
,[Freight]
FROM [SalesLT].[SalesOrderHeader]
)
UPDATE SalesData SET [Freight]=100.00 WHERE [SalesOrderID]=71774
Go
Ta bort uttalande i 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
Flera CTE
Du kan deklarera flera CTE:er i T-SQL-skriptet och använda join-operationerna på dem. För multipel CTE använder T-SQL ett kommatecken som avgränsare.
I följande fråga har vi två CTE:
- CTESales
- CTESalesDescription
Senare, i select-satsen, hämtar vi resultat med INNER JOIN på båda 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]
Rekursiva vanliga tabelluttryck
Den rekursiva CTE körs i en upprepad procedurloop tills villkoret uppfylls. Följande T-SQL-exempel använder en ID-räknare och väljer poster tills WHERE-villkoret är uppfyllt.
Declare @ID int =1;
;with RecursiveCTE as
(
SELECT @ID as ID
UNION ALL
SELECT ID+ 1
FROM RecursiveCTE
WHERE ID <5
)
SELECT * FROM RecursiveCTE
En annan användning av rekursiv CTE i SQL Server är att visa hierarkisk data. Antag att vi har en anställd tabell, och den har register för alla anställda, deras avdelningar och deras chefers ID.
--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);
Nu måste vi generera personalhierarkidata. Vi kan använda rekursiv CTE med UNION ALL i select-satsen.
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;
CTE returnerar uppgifterna på anställd nivå som visas nedan.
Viktiga punkter angående vanliga tabelluttryck
- Vi kan inte återanvända CTE. Dess omfattning är begränsad till de yttre SELECT-, INSERT-, UPDATE- eller MERGE-satserna.
- Du kan använda flera CTES; de bör dock använda operatorerna UNION ALL, UNION, INTERSECT eller EXCERPT.
- Vi kan definiera flera CTE-frågedefinitioner i den icke-rekursiva CTE.
- Vi kan inte använda ORDER BY (utan TOP), INTO, OPTIONS-satsen med frågetips och FOR Browse i CTE-frågedefinitionen.
Till exempel använder skriptet nedan ORDER BY-satsen utan en TOP-sats.
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
Det ger följande fel:
- Vi kan inte skapa ett index på CTE.
- De definierade kolumnnamnen i CTE bör matcha kolumnerna som returneras i select-satsen.
CTE har inte kolumnen [Phone] i koden nedan medan select-satsen returnerar sitt värde. Därför får du det markerade felmeddelandet.
- Om du har flera satser i T-SQL-skriptet, bör den föregående satsen före CTE avslutas med semikolon.
Till exempel innehåller den första select-satsen inget semikolon. Därför får du ett felaktigt syntaxfel i CTE-skriptet.
Skriptet fungerar bra om vi avslutar den första select-satsen med semikolonoperatorn.
- Vi kan inte använda en dubblettkolumn i select-satsen om vi inte deklarerar kolumnnamnet externt.
Till exempel anger följande CTE-definition dubblettkolumnen [Telefon]. Det returnerar ett fel.
Men om du definierar externa kolumner kommer det inte att orsaka fel. Det krävs när du behöver en enstaka kolumn flera gånger i utdata för olika beräkningar.
Viktigt:Common Table Expressions (CTE) är inte en ersättning för temporära tabeller eller tabellvariabler.
- Templariska tabeller skapas i TempDB, och vi kan definiera indexbegränsningar som liknar en vanlig tabell. Vi kan inte referera till temptabellen flera gånger under en session
- Tabellvariabler finns också i TempDB och fungerar som variabler som existerar under batchkörningen. Vi kan inte definiera ett index på tabellvariablerna.
- CTE är avsedd för en enda referens, och vi kan inte definiera indexet på det. Den finns i minnet och släpps på efter referens.
Slutsats
Common Table Expressions (CTE) låter utvecklarna skriva ren och effektiv kod. I allmänhet kan du använda CTE där du inte behöver flera referenser som en temporär tabell, och vi utforskade olika scenarier för SELECT, INSERT, UPDATE, DETELTE-sats och rekursiva CTE:er.
Med hjälp av moderna verktyg, som SQL Complete SSMS Add-in, blir hanteringen av CTE:er ännu enklare. Tillägget kan föreslå CTEs i farten, vilket gör uppgifterna som involverar CTE mycket enklare. Se även Microsofts dokumentation för mer information om CTE.