Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Leitfaden für CTE in SQL Server

Der Allgemeine Tabellenausdruck auch bekannt als CTE in SQL Server stellt eine temporäre Ergebnismenge in T-SQL bereit. Sie können in einer SQL Select-, SQL Insert-, SQL Delete- oder SQL Update-Anweisung darauf verweisen.

Die Option ist ab SQL Server 2005 verfügbar und unterstützt Entwickler beim Schreiben komplexer und langer Abfragen mit vielen JOINs, Aggregation und Datenfilterung. Normalerweise verwenden Entwickler Unterabfragen zum Schreiben von T-SQL-Codes, und SQL Server speichert diese CTE vorübergehend im Arbeitsspeicher, bis die Abfrageausführung abgeschlossen ist. Sobald die Abfrage beendet ist, wird sie aus dem Speicher entfernt.

CTE in SQL Server:Syntax

WITH <common_table_expression> ([column names])
AS
(
   <query_definition>
)
<operation>
  • Es verwendet einen CTE-Namen, um darauf zu verweisen, um die Select-, Insert-, Update-, Delete- oder Merge-Anweisungen auszuführen.
  • Spaltennamen sind durch Kommas getrennt. Sie sollten mit den in der Abfragedefinition definierten Spalten übereinstimmen.
  • Die Abfragedefinition umfasst die Select-Anweisungen aus einer einzelnen Tabelle oder Joins zwischen mehreren Tabellen.
  • Sie können auf den Namen des CTE-Ausdrucks verweisen, um die Ergebnisse abzurufen.

Beispielsweise verwendet die folgende grundlegende CTE-Abfrage die folgenden Teile:

  • Allgemeiner Tabellenausdrucksname – SalesCustomerData
  • Spaltenliste – [Kundennummer],[Vorname],[Nachname],[Firmenname],[E-Mail-Adresse],[Telefon]
  • Die Abfragedefinition enthält eine Select-Anweisung, die Daten aus der Tabelle [SalesLT].[Customer] abruft
  • Der letzte Teil verwendet die select-Anweisung für den CTE-Ausdruck und filtert Datensätze mithilfe der where-Klausel.
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 einem anderen Beispiel berechnen wir den durchschnittlichen Gesamtumsatz aus dem CTE. Die Abfragedefinition enthält die GROUP BY-Klausel. Später verwenden wir die AVG()-Funktion zur Berechnung des Durchschnittswerts.

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

Sie können CTE auch verwenden, um Daten in die SQL-Tabelle einzufügen. Die CTE-Abfragedefinition enthält die erforderlichen Daten, die Sie mithilfe von Verknüpfungen aus vorhandenen Tabellen abrufen können. Fragen Sie später CTE ab, um Daten in die Zieltabelle einzufügen.

Hier verwenden wir die Anweisung SELECT INTO, um eine neue Tabelle mit dem Namen [CTETest] aus der Ausgabe der Anweisung CTE select zu erstellen.

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

Sie können auch eine vorhandene Tabelle angeben, die den Spalten mit den eingefügten Daten entspricht.

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

Sie können Datensätze in der SQL-Tabelle auch mit dem allgemeinen Tabellenausdruck aktualisieren oder löschen. Die folgenden Abfragen verwenden DELETE- und UPDATE-Anweisungen mit CTE.

Aktualisierung der Erklärung in CTE

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

Anweisung in CTE löschen

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

Mehrere CTEs

Sie können mehrere CTEs im T-SQL-Skript deklarieren und die Join-Operationen darauf anwenden. Für den Mehrfach-CTE verwendet T-SQL ein Komma als Trennzeichen.

In der folgenden Abfrage haben wir zwei CTEs:

  1. CTE-Verkäufe
  2. CTESalesDescription

Später, in der select-Anweisung, rufen wir Ergebnisse mit INNER JOIN auf beiden CTEs ab.

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]

Rekursive allgemeine Tabellenausdrücke

Der rekursive CTE wird in einer wiederholten Prozedurschleife ausgeführt, bis die Bedingung erfüllt ist. Das folgende T-SQL-Beispiel verwendet einen ID-Zähler und wählt Datensätze aus, bis die WHERE-Bedingung erfüllt ist.

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

Eine weitere Verwendung des rekursiven CTE in SQL Server ist die Anzeige hierarchischer Daten. Angenommen, wir haben einen Mitarbeiter Tabelle und enthält Datensätze für alle Mitarbeiter, ihre Abteilungen und die IDs ihrer 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);

Jetzt müssen wir die Mitarbeiterhierarchiedaten generieren. Wir können rekursiven CTE mit UNION ALL in der Select-Anweisung verwenden.

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;

Der CTE gibt die Details auf Mitarbeiterebene wie unten gezeigt zurück.

Wichtige Punkte zu gebräuchlichen Tabellenausdrücken

  • Wir können den CTE nicht wiederverwenden. Sein Gültigkeitsbereich ist auf die äußeren SELECT-, INSERT-, UPDATE- oder MERGE-Anweisungen beschränkt.
  • Sie können mehrere CTES verwenden; Sie sollten jedoch die Operatoren UNION ALL, UNION, INTERSECT oder EXCERPT verwenden.
  • Wir können mehrere CTE-Abfragedefinitionen im nicht-rekursiven CTE definieren.
  • Wir können ORDER BY (ohne TOP), INTO, OPTIONS-Klausel mit Abfragehinweisen und FOR BROWSE nicht in der CTE-Abfragedefinition verwenden.

Das folgende Skript verwendet beispielsweise die ORDER BY-Klausel ohne eine TOP-Klausel.

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

Es gibt den folgenden Fehler:

  • Wir können keinen Index auf dem CTE erstellen.
  • Die definierten Spaltennamen in CTE sollten mit den Spalten übereinstimmen, die in der Select-Anweisung zurückgegeben werden.

Der CTE hat im folgenden Code nicht die Spalte [Phone], während die select-Anweisung ihren Wert zurückgibt. Daher erhalten Sie die hervorgehobene Fehlermeldung.

  • Wenn Sie mehrere Anweisungen im T-SQL-Skript haben, sollte die vorherige Anweisung vor CTE mit einem Semikolon enden.

Beispielsweise enthält die erste select-Anweisung kein Semikolon. Daher erhalten Sie einen falschen Syntaxfehler im CTE-Skript.

Das Skript funktioniert einwandfrei, wenn wir die erste select-Anweisung mit dem Semikolon-Operator abschließen.

  • Wir können keine doppelte Spalte in der Select-Anweisung verwenden, wenn wir den Spaltennamen nicht extern deklarieren.

Beispielsweise gibt die folgende CTE-Definition die doppelte Spalte [Telefon] an. Es gibt einen Fehler zurück.

Wenn Sie jedoch externe Spalten definieren, führt dies nicht zu Fehlern. Es ist erforderlich, wenn Sie eine einzelne Spalte mehrmals in der Ausgabe für verschiedene Berechnungen benötigen.

Wichtig:Common Table Expressions (CTE) sind kein Ersatz für temporäre Tabellen oder Tabellenvariablen.

  • Temp-Tabellen werden in der TempDB erstellt, und wir können Indexbeschränkungen ähnlich wie bei einer regulären Tabelle definieren. Wir können nicht mehrmals in einer Sitzung auf die temporäre Tabelle verweisen
  • Tabellenvariablen existieren auch in der TempDB und verhalten sich wie Variablen, die während der Batch-Ausführung existieren. Wir können keinen Index für die Tabellenvariablen definieren.
  • CTE dient einem einzigen Referenzzweck und wir können den Index nicht darauf definieren. Es ist im Speicher vorhanden und wird nach dem Verweis gelöscht.

Schlussfolgerung

Die Common Table Expressions (CTE) ermöglichen es den Entwicklern, sauberen und effektiven Code zu schreiben. Im Allgemeinen können Sie CTE verwenden, wenn Sie nicht mehrere Verweise wie eine temporäre Tabelle benötigen, und wir haben verschiedene Szenarien für SELECT-, INSERT-, UPDATE-, DETELTE-Anweisungen und rekursive CTEs untersucht.

Mithilfe moderner Tools wie dem SQL Complete SSMS Add-in wird der Umgang mit CTEs noch einfacher. Das Add-In kann spontan CTEs vorschlagen, wodurch die Aufgaben mit CTE viel einfacher werden. Weitere Einzelheiten zum CTE finden Sie auch in der Microsoft-Dokumentation.