sql >> Databáze >  >> RDS >> Sqlserver

Průvodce pro CTE v SQL Server

Common Table Expression aka CTE v SQL Server poskytuje dočasnou sadu výsledků v T-SQL. Můžete na něj odkazovat v příkazu SQL Select, SQL Insert, SQL Delete nebo SQL Update.

Tato možnost je k dispozici od SQL Server 2005 a novější a pomáhá vývojářům psát složité a dlouhé dotazy zahrnující mnoho spojení JOIN, agregaci a filtrování dat. Vývojáři obvykle používají poddotazy pro psaní kódů T-SQL a SQL Server dočasně ukládá tyto CTE do paměti, dokud neskončí provádění dotazu. Jakmile je dotaz dokončen, je odstraněn z paměti.

CTE v SQL Server:Syntaxe

WITH <common_table_expression> ([column names])
AS
(
   <query_definition>
)
<operation>
  • Pro provádění příkazů Select, Insert, Update, Delete nebo Merge používá název CTE.
  • Názvy sloupců jsou odděleny čárkami. Měly by odpovídat sloupcům definovaným v definici dotazu.
  • Definice dotazu zahrnuje příkazy select z jedné tabulky nebo spojení mezi více tabulkami.
  • Pro získání výsledků můžete použít název výrazu CTE.

Například následující základní dotaz CTE používá následující části:

  • Název běžného výrazu tabulky – SalesCustomerData
  • Seznam sloupců – [CustomerID],[FirstName],[LastName],[CompanyName],[EmailAddress],[Phone]
  • Definice dotazu zahrnuje příkaz select, který získává data z tabulky [SalesLT].[Customer]
  • Poslední část používá příkaz select u výrazu CTE a filtruje záznamy pomocí klauzule 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

V dalším příkladu vypočítáme průměrné celkové tržby z CTE. Definice dotazu obsahuje klauzuli GROUP BY. Později použijeme funkci AVG() pro výpočet průměrné hodnoty.

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

CTE můžete také použít k vložení dat do SQL tabulky. Definice dotazu CTE obsahuje požadovaná data, která můžete načíst z existujících tabulek pomocí spojení. Později požádejte CTE o vložení dat do cílové tabulky.

Zde používáme příkaz SELECT INTO k vytvoření nové tabulky s názvem [CTETest] z výstupu příkazu 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

Můžete také určit existující tabulku, která odpovídá sloupcům s vloženými daty.

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

Záznamy v tabulce SQL můžete aktualizovat nebo mazat také pomocí běžného tabulkového výrazu. Následující dotazy používají příkazy DELETE a UPDATE s CTE.

Aktualizovat prohlášení v CTE

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

Smazat výpis v 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

Více CTE

Ve skriptu T-SQL můžete deklarovat více CTE a používat na nich operace spojení. Pro více CTE používá T-SQL jako oddělovač čárku.

V následujícím dotazu máme dva CTE:

  1. CTESales
  2. CTESalesDescription

Později, v příkazu select, získáme výsledky pomocí INNER JOIN na obou 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]

Rekurzivní běžné tabulkové výrazy

Rekurzivní CTE běží v opakované procedurální smyčce, dokud podmínka nevyhoví. Následující příklad T-SQL používá čítač ID a vybírá záznamy, dokud není splněna podmínka 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

Dalším využitím rekurzivního CTE v SQL Server je zobrazení hierarchických dat. Předpokládejme, že máme zaměstnance a obsahuje záznamy pro všechny zaměstnance, jejich oddělení a ID jejich manažerů.

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

Nyní musíme vygenerovat data hierarchie zaměstnanců. Můžeme použít rekurzivní CTE s UNION ALL v příkazu 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 vrátí podrobnosti o úrovni zaměstnance, jak je uvedeno níže.

Důležité body týkající se běžných tabulkových výrazů

  • CTE nemůžeme znovu použít. Jeho rozsah je omezen na vnější příkazy SELECT, INSERT, UPDATE nebo MERGE.
  • Můžete použít více CTES; měli by však používat operátory UNION ALL, UNION, INTERSECT nebo EXCERPT.
  • V nerekurzivním CTE můžeme definovat více definic dotazů CTE.
  • V definici dotazu CTE nemůžeme použít klauzuli ORDER BY (bez TOP), INTO, OPTIONS s nápovědou k dotazu a FOR BROWSE.

Například níže uvedený skript používá klauzuli ORDER BY bez klauzule 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

Hlásí to následující chybu:

  • Nemůžeme vytvořit index na CTE.
  • Názvy definovaných sloupců v CTE by se měly shodovat se sloupci vrácenými v příkazu select.

CTE nemá v níže uvedeném kódu sloupec [Phone], zatímco příkaz select vrací jeho hodnotu. Proto se zobrazí zvýrazněná chybová zpráva.

  • Pokud máte ve skriptu T-SQL více příkazů, měl by předchozí příkaz před CTE končit středníkem.

Například první příkaz select neobsahuje středník. Proto se zobrazí nesprávná syntaktická chyba ve skriptu CTE.

Skript funguje dobře, pokud ukončíme první příkaz select pomocí operátoru středníku.

  • Nemůžeme použít duplicitní sloupec v příkazu select, pokud externě nedeklarujeme název sloupce.

Například následující definice CTE určuje duplicitní sloupec [Telefon]. Vrací chybu.

Pokud však definujete externí sloupce, nezpůsobí to chyby. Je vyžadováno, když potřebujete jeden sloupec vícekrát ve výstupu pro různé výpočty.

Důležité:Common Table Expressions (CTE) nenahrazují dočasné tabulky nebo proměnné tabulky.

  • Tabulky Temp se vytvářejí v TempDB a můžeme definovat omezení indexu podobná běžné tabulce. Nemůžeme odkazovat na dočasnou tabulku vícekrát během relace
  • Proměnné tabulky také existují v TempDB a fungují jako proměnné, které existují během dávkového provádění. Nemůžeme definovat index pro proměnné tabulky.
  • CTE je pro jediný referenční účel a nemůžeme na něm definovat index. Existuje v paměti a po vytvoření odkazu je vynechán.

Závěr

Common Table Expressions (CTE) umožňují vývojářům psát čistý a efektivní kód. Obecně platí, že CTE můžete použít tam, kde nepotřebujete více odkazů, jako je dočasná tabulka, a prozkoumali jsme různé scénáře pro příkazy SELECT, INSERT, UPDATE, DETELTE a rekurzivní CTE.

S pomocí moderních nástrojů, jako je SQL Complete SSMS Add-in, je manipulace s CTE ještě jednodušší. Doplněk může navrhovat CTE za běhu, takže úkoly týkající se CTE jsou mnohem jednodušší. Další podrobnosti o CTE naleznete také v dokumentaci společnosti Microsoft.


  1. Jak spojit dvě tabulky v MySQL

  2. OBJEDNAT BY Alias ​​nefunguje

  3. Jak opravit výstrahu Security Advisor MySQL

  4. Při instalaci pg (0.17.1) došlo k chybě a Bundler nemůže pokračovat