Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Guia para CTE no SQL Server


A Expressão de tabela comum também conhecido como CTE no SQL Server fornece um conjunto de resultados temporário em T-SQL. Você pode consultá-lo em uma instrução SQL Select, SQL Insert, SQL Delete ou SQL Update.

A opção está disponível a partir do SQL Server 2005, ajudando os desenvolvedores a escrever consultas complexas e longas envolvendo muitos JOINs, agregação e filtragem de dados. Normalmente, os desenvolvedores usam subconsultas para escrever códigos T-SQL e o SQL Server armazena esses CTEs na memória temporariamente até que a execução da consulta termine. Quando a consulta é concluída, ela é removida da memória.

CTE no SQL Server:Sintaxe

WITH <common_table_expression> ([column names])
AS
(
   <query_definition>
)
<operation>
  • Ele usa um nome CTE para se referir a ele para executar as instruções Select, Insert, Update, Delete ou Merge.
  • Os nomes das colunas são separados por vírgulas. Eles devem corresponder às colunas definidas na definição da consulta.
  • A definição da consulta envolve as instruções select de uma única tabela ou junções entre várias tabelas.
  • Você pode consultar o nome da expressão CTE para recuperar os resultados.

Por exemplo, a seguinte consulta CTE básica usa as seguintes partes:
  • Nome da expressão de tabela comum – SalesCustomerData
  • Lista de colunas – [CustomerID],[FirstName],[LastName],[CompanyName],[EmailAddress],[Phone]
  • A definição da consulta inclui uma instrução select que obtém dados da tabela [SalesLT].[Customer]
  • A última parte usa a instrução select na expressão CTE e filtra os registros usando a cláusula 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

Em outro exemplo, calculamos a média total de vendas do CTE. A definição de consulta inclui a cláusula GROUP BY. Posteriormente, usamos a função AVG() para calcular o valor médio.
WITH Salesdata ([SalesOrderID],[Total])
AS(
SELECT [SalesOrderID]
         ,count(*) AS total
          FROM [SalesLT].[SalesOrderHeader]
        GROUP BY [SalesOrderID]
)
SELECT avg(total) FROM salesdata

Você também pode usar CTE para inserir dados na tabela SQL. A definição de consulta CTE inclui os dados necessários que você pode buscar de tabelas existentes usando junções. Posteriormente, consulte o CTE para inserir dados na tabela de destino.

Aqui usamos a instrução SELECT INTO para criar uma nova tabela chamada [CTETest] a partir da saída da instrução 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

Você também pode especificar uma tabela existente que corresponda às colunas com os dados inseridos.
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

Você também pode atualizar ou excluir registros na tabela SQL usando a expressão de tabela comum. As consultas a seguir usam instruções DELETE e UPDATE com CTE.

Atualizar Declaração no CTE

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

Excluir extrato no 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ários CTEs


Você pode declarar vários CTEs no script T-SQL e usar as operações de junção neles. Para o CTE múltiplo, o T-SQL usa uma vírgula como separador.

Na consulta a seguir, temos dois CTEs:
  1. CTESVendas
  2. CTESDescrição de vendas

Mais tarde, na instrução select, recuperamos os resultados usando INNER JOIN em ambos os CTEs.
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]

Expressões de tabela comuns recursivas


O CTE recursivo é executado em um loop procedural repetido até que a condição seja satisfeita. O exemplo de T-SQL a seguir usa um contador de ID e seleciona registros até que a condição WHERE seja satisfeita.
Declare @ID int =1;
;with RecursiveCTE as  
   (  
      SELECT @ID as ID
        UNION ALL  
      SELECT  ID+ 1
  FROM  RecursiveCTE  
  WHERE ID <5
    )  
 
SELECT * FROM RecursiveCTE

Outro uso da CTE recursiva no SQL Server é exibir dados hierárquicos. Suponha que temos um funcionário e tem registros para todos os funcionários, seus departamentos e IDs de seus gerentes.
--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);

Agora, precisamos gerar os dados de hierarquia de funcionários. Podemos usar CTE recursiva com UNION ALL na instrução 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;

O CTE retorna os detalhes do nível do funcionário, conforme mostrado abaixo.

Pontos importantes sobre expressões de tabela comuns

  • Não podemos reutilizar o CTE. Seu escopo é limitado às instruções externas SELECT, INSERT, UPDATE ou MERGE.
  • Você pode usar vários CTES; no entanto, eles devem usar os operadores UNION ALL, UNION, INTERSECT ou EXCERPT.
  • Podemos definir várias definições de consulta CTE no CTE não recursivo.
  • Não podemos usar a cláusula ORDER BY (sem TOP), INTO, OPTIONS com dicas de consulta e FOR BROWSE na definição de consulta CTE.

Por exemplo, o script abaixo usa a cláusula ORDER BY sem uma cláusula 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á o seguinte erro:
  • Não podemos criar um índice no CTE.
  • Os nomes de coluna definidos no CTE devem corresponder às colunas retornadas na instrução select.

O CTE não possui a coluna [Phone] no código abaixo enquanto a instrução select retorna seu valor. Portanto, você recebe a mensagem de erro realçada.
  • Se você tiver várias instruções no script T-SQL, a instrução anterior antes de CTE deve terminar usando um ponto e vírgula.

Por exemplo, a primeira instrução select não inclui um ponto e vírgula. Portanto, você recebe um erro de sintaxe incorreto no roteiro CTE.

O script funciona bem se terminarmos a primeira instrução select usando o operador ponto-e-vírgula.
  • Não podemos usar uma coluna duplicada na instrução select se não declararmos o nome da coluna externamente.

Por exemplo, a definição de CTE a seguir especifica a coluna duplicada [Phone]. Ele retorna um erro.

No entanto, se você definir colunas externas, isso não causará erros. É necessário quando você precisa de uma única coluna várias vezes na saída para cálculos diferentes.

Importante:Expressões de Tabela Comuns (CTE) não substituem as tabelas temporárias ou variáveis ​​de tabela.
  • As tabelas temporárias são criadas no TempDB e podemos definir restrições de índice semelhantes a uma tabela regular. Não podemos fazer referência à tabela temporária várias vezes em uma sessão
  • As variáveis ​​de tabela também existem no TempDB e atuam como variáveis ​​que existem durante a execução em lote. Não podemos definir um índice nas variáveis ​​da tabela.
  • CTE é para um único propósito de referência e não podemos definir o índice nele. Ele existe na memória e é descartado após a referência ser feita.

Conclusão


As Expressões de Tabela Comum (CTE) permitem que os desenvolvedores escrevam código limpo e eficaz. Em geral, você pode usar o CTE onde não precisar de várias referências, como uma tabela temporária, e exploramos vários cenários para instruções SELECT, INSERT, UPDATE, DETELTE e CTEs recursivos.

Com a ajuda de ferramentas modernas, como o SQL Complete SSMS Add-in, o manuseio de CTEs fica ainda mais fácil. O add-in pode sugerir CTEs em tempo real, tornando as tarefas que envolvem CTE muito mais simples. Além disso, consulte a documentação da Microsoft para obter mais detalhes sobre o CTE.