Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Przewodnik po CTE w SQL Server

Wspólne wyrażenie tabelowe aka CTE w SQL Server zapewnia tymczasowy zestaw wyników w T-SQL. Możesz się do niego odnieść w instrukcjach SQL Select, SQL Insert, SQL Delete lub SQL Update.

Ta opcja jest dostępna od SQL Server 2005 i umożliwia programistom pisanie złożonych i długich zapytań obejmujących wiele sprzężeń, agregację i filtrowanie danych. Zwykle programiści używają podzapytań do pisania kodów T-SQL, a SQL Server przechowuje te CTE tymczasowo w pamięci, aż do zakończenia wykonywania zapytania. Po zakończeniu zapytania jest ono usuwane z pamięci.

CTE w SQL Server:składnia

WITH <common_table_expression> ([column names])
AS
(
   <query_definition>
)
<operation>
  • Używa nazwy CTE, aby odnosić się do niego podczas wykonywania instrukcji Select, Insert, Update, Delete lub Merge.
  • Nazwy kolumn są oddzielone przecinkami. Powinny odpowiadać kolumnom zdefiniowanym w definicji zapytania.
  • Definicja zapytania obejmuje instrukcje select z jednej tabeli lub połączenia między wieloma tabelami.
  • Aby pobrać wyniki, możesz odwołać się do nazwy wyrażenia CTE.

Na przykład poniższe podstawowe zapytanie CTE zawiera następujące części:

  • Wspólna nazwa wyrażenia tabelowego — SalesCustomerData
  • Lista kolumn – [ID klienta],[Imię],[Nazwisko],[Nazwa firmy],[Adres e-mail],[Telefon]
  • Definicja zapytania zawiera instrukcję select, która pobiera dane z tabeli [SalesLT].[Klient]
  • Ostatnia część używa instrukcji select w wyrażeniu CTE i filtruje rekordy za pomocą klauzuli 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

W innym przykładzie obliczamy średnią całkowitą sprzedaż z CTE. Definicja zapytania zawiera klauzulę GROUP BY. Później użyjemy funkcji AVG() do obliczenia średniej wartości.

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

Możesz także użyć CTE do wstawienia danych do tabeli SQL. Definicja zapytania CTE zawiera wymagane dane, które można pobrać z istniejących tabel za pomocą złączeń. Później zapytaj CTE o wstawienie danych do tabeli docelowej.

Tutaj używamy instrukcji SELECT INTO, aby utworzyć nową tabelę o nazwie [CTETest] z danych wyjściowych instrukcji 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

Możesz także określić istniejącą tabelę, która pasuje do kolumn z wstawionymi danymi.

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

Rekordy w tabeli SQL można aktualizować lub usuwać również za pomocą wspólnego wyrażenia tabelowego. Poniższe zapytania używają instrukcji DELETE i UPDATE z CTE.

Oświadczenie o aktualizacji w CTE

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

Usuń oświadczenie w 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

Wiele CTE

Możesz zadeklarować wiele CTE w skrypcie T-SQL i użyć na nich operacji łączenia. W przypadku wielu CTE T-SQL używa przecinka jako separatora.

W poniższym zapytaniu mamy dwa CTE:

  1. CTEsprzedaż
  2. Opis sprzedaży CTE

Później, w instrukcji select, pobieramy wyniki za pomocą INNER JOIN dla obu 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]

Rekurencyjne wyrażenia typowych tabel

Rekursywne CTE działa w powtarzającej się pętli proceduralnej, dopóki warunek nie zostanie spełniony. Poniższy przykład T-SQL używa licznika ID i wybiera rekordy aż do spełnienia warunku WHERE.

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

Innym zastosowaniem rekurencyjnego CTE w SQL Server jest wyświetlanie danych hierarchicznych. Załóżmy, że mamy pracownika tabeli i zawiera rekordy dla wszystkich pracowników, ich działów i identyfikatorów menedżerów.

--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);

Teraz musimy wygenerować dane hierarchii pracowników. Możemy użyć rekurencyjnego CTE z UNION ALL w instrukcji 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;

CTE zwraca szczegóły poziomu pracownika, jak pokazano poniżej.

Ważne punkty dotyczące typowych wyrażeń tabel

  • Nie możemy ponownie wykorzystać CTE. Jego zakres jest ograniczony do zewnętrznych instrukcji SELECT, INSERT, UPDATE lub MERGE.
  • Możesz użyć wielu CTES; jednak powinny używać operatorów UNION ALL, UNION, INTERSECT lub EXCERPT.
  • Możemy zdefiniować wiele definicji zapytań CTE w nierekurencyjnym CTE.
  • Nie możemy użyć klauzuli ORDER BY (bez TOP), INTO, OPTIONS z podpowiedziami do zapytania ani FOR BROWSE w definicji zapytania CTE.

Na przykład poniższy skrypt używa klauzuli ORDER BY bez klauzuli 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

Daje następujący błąd:

  • Nie możemy utworzyć indeksu CTE.
  • Zdefiniowane nazwy kolumn w CTE powinny odpowiadać kolumnom zwróconym w instrukcji SELECT.

CTE nie zawiera kolumny [Phone] w poniższym kodzie, podczas gdy instrukcja select zwraca jej wartość. Dlatego otrzymujesz podświetlony komunikat o błędzie.

  • Jeśli masz wiele instrukcji w skrypcie T-SQL, poprzednia instrukcja przed CTE powinna kończyć się za pomocą średnika.

Na przykład pierwsza instrukcja select nie zawiera średnika. Dlatego otrzymujesz nieprawidłowy błąd składni w skrypcie CTE.

Skrypt działa dobrze, jeśli zakończymy pierwszą instrukcję select za pomocą operatora średnika.

  • Nie możemy użyć zduplikowanej kolumny w instrukcji select, jeśli nie zadeklarujemy nazwy kolumny zewnętrznie.

Na przykład poniższa definicja CTE określa zduplikowaną kolumnę [Telefon]. Zwraca błąd.

Jeśli jednak zdefiniujesz kolumny zewnętrzne, nie spowoduje to błędów. Jest to wymagane, gdy potrzebujesz wiele razy jednej kolumny w wynikach dla różnych obliczeń.

Ważne:Common Table Expressions (CTE) nie zastępują tabel tymczasowych ani zmiennych tabel.

  • Tabele Temp są tworzone w TempDB i możemy zdefiniować ograniczenia indeksu podobne do zwykłej tabeli. Nie możemy wielokrotnie odwoływać się do tabeli tymczasowej podczas sesji
  • Zmienne tabeli istnieją również w bazie danych TempDB i działają jak zmienne, które istnieją podczas wykonywania wsadowego. Nie możemy zdefiniować indeksu w zmiennych tabeli.
  • CTE służy do pojedynczego odniesienia i nie możemy zdefiniować w nim indeksu. Istnieje w pamięci i jest usuwany po dokonaniu odniesienia.

Wniosek

Common Table Expressions (CTE) umożliwiają programistom pisanie czystego i efektywnego kodu. Ogólnie rzecz biorąc, możesz używać CTE tam, gdzie nie potrzebujesz wielu odwołań, takich jak tabela tymczasowa, a my zbadaliśmy różne scenariusze dla instrukcji SELECT, INSERT, UPDATE, DETELTE i rekurencyjnych CTE.

Przy pomocy nowoczesnych narzędzi, takich jak dodatek SQL Complete SSMS, obsługa CTE staje się jeszcze łatwiejsza. Dodatek może na bieżąco sugerować CTE, dzięki czemu zadania związane z CTE są znacznie prostsze. Więcej informacji na temat CTE można znaleźć w dokumentacji firmy Microsoft.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Dostrajanie SQL Server – chodzi o pomiar

  2. Utwórz datę z dnia, miesiąca i roku za pomocą T-SQL

  3. Nazwy plików SQL Server a wersje

  4. 7 sposobów na zwrócenie wszystkich tabel z kluczami obcymi w SQL Server

  5. serwer sql niepoprawna nazwa obiektu - ale tabele są wymienione na liście tabel SSMS