Database
 sql >> Base de Dados >  >> RDS >> Database

Tudo o que você precisa saber sobre SQL CTE em um só lugar


A primeira vez que Karl ouviu falar do SQL Server CTE foi quando estava procurando algo para tornar seu código SQL mais fácil para os olhos. É uma espécie de dor de cabeça quando você olha para isso. Anton, seu colega preocupado, perguntou-lhe sobre o CTE. Karl pensou que Anton estava se referindo à sua dor de cabeça. Talvez ele tenha ouvido tudo errado, então respondeu:“Claro que não”. O engraçado é que ele estava se referindo à Encefalopatia Traumática Crônica, também uma CTE – uma doença neurodegenerativa causada por repetidas lesões na cabeça. Mas, com base na resposta de Karl, Anton sabia com certeza que seu colega não fazia ideia do que ele estava dizendo.

Que maneira louca de introduzir CTEs! Então, antes de entrar no mesmo barco, vamos esclarecer, o que é SQL CTE ou Common Table Expressions no mundo SQL?

Você pode ler o básico aqui. Enquanto isso, aprenderemos um pouco mais sobre o que aconteceu nesta história incomum.

4 coisas básicas sobre CTE no SQL Server

"Um SQL CTE tem um nome"


Anton começou com a ideia de que CTEs SQL são conjuntos de resultados nomeados temporariamente. Sendo um meio temporário, o CTE tem um alcance limitado.

"Então, é como uma subconsulta?" perguntou Karl.

“De certa forma, sim. Mas você não pode nomear uma subconsulta”, disse Anton. “Um CTE tem um nome muito parecido com uma tabela com um nome. No entanto, em vez de CREATE, você usa WITH para criá-lo.” Então, ele escreveu a sintaxe no papel:
WITH <cte_name>(<column list>)
AS
(
<inner query defining the CTE>
)
<outer query against CTE>

"A CTE desaparece quando o SELECT é concluído"


Anton continuou explicando o escopo do SQL CTE.

“Uma tabela temporária pode existir dentro do escopo do procedimento ou globalmente. Mas o CTE se foi quando o SELECT terminar”, disse ele com uma rima. “A mesma coisa se você usá-lo para INSERT, UPDATE ou DELETE”, continuou ele.

"Você não pode reutilizá-lo"


“Ao contrário de uma visualização ou tabela temporária, você não pode reutilizar o SQL CTE. O nome está lá, então você pode se referir a ele na consulta interna e externa. Mas isso é tudo”, disse Anton.

“Então, qual é o problema dos CTEs SQL?” perguntou Karl.

"Você pode tornar seu código mais legível"


"O grande negócio?" Anton retornou a pergunta. “É que você pode tornar seu código facilmente legível. Não é isso que você está procurando?”

"Isso mesmo", admitiu Karl.

Então, qual é o próximo passo lógico para Karl fazer?

Coisas extras sobre CTE em SQL


No dia seguinte, Karl continuou sua busca pelo SQL CTE. Além do acima, aqui está o que ele encontrou:
  • O SQL CTE pode ser não recursivo ou recursivo.
  • Não apenas o SQL Server, mas também o MySQL e o Oracle apoiam a ideia. É, na verdade, uma parte das especificações do SQL-99.
  • Embora seja usado para simplificar o código SQL, ele não melhora o desempenho.
  • E também não substituirá subconsultas e tabelas temporárias. Cada um tem seu lugar e uso.

Em resumo, é outra maneira de expressar uma consulta .

Mas Karl estava faminto por mais detalhes, então continuou procurando o que funcionaria, o que não funcionaria e como funcionaria versus subconsultas e tabelas temporárias.

O que funcionará no SQL Server CTE?


Indo mais longe para desvendar mais sobre o CTE, Karl listou abaixo o que o SQL Server aceitaria. Dê uma olhada em seus estudos também.

Atribuir aliases de coluna embutidos ou externos


SQL CTEs suportam duas formas de atribuição de aliases de coluna. O primeiro é o formulário embutido, como no exemplo abaixo:
-- Use an Inline column alias

USE AdventureWorks
GO;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,a.NumberOfOrders
FROM Sales_CTE a

O código acima usa alias de coluna na definição CTE quando é atribuído na instrução SELECT. Você notou o COUNT(*) AS NumberOfOrders ? Esse é o formulário embutido.

Agora, outro exemplo é o formulário externo:
-- Use an external column alias

USE AdventureWorks
GO;

WITH Sales_CTE(SalesPersonID, NumberOfOrders) 
AS  
(  
	SELECT SalesPersonID, COUNT(*)
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,a.NumberOfOrders
FROM Sales_CTE a

As colunas também podem ser definidas entre parênteses após definir o nome do CTE. Observe o WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE em SQL precede um SELECT, INSERT, UPDATE ou DELETE


Este próximo item é sobre o consumo do CTE. O primeiro e comum exemplo é quando ele precede uma instrução SELECT.
-- List down all Salespersons with their all-time number of orders
USE AdventureWorks
GO;

WITH Sales_CTE (SalesPersonID, NumberOfOrders)  
AS  
(  
	SELECT SalesPersonID, COUNT(*)  
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,CONCAT(P.LastName,', ',P.FirstName,' ',P.MiddleName) AS SalesPerson
,a.NumberOfOrders
FROM Sales_CTE a
INNER JOIN Person.Person p ON a.SalesPersonID = p.BusinessEntityID

O que este exemplo mostra?
  • Vendas_CTE – o nome do CTE.
  • (VendasPessoaID, NúmeroDePedidos) – a definição de colunas CTE.
  • SELECT SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader WHERE SalesPersonID NÃO É NULL GROUP BY SalesPersonID – o SELECT interno que define o CTE.
  • SELECT a.SalesPersonID, CONCAT(P.LastName,', ',P.FirstName,' ',P.MiddleName) AS SalesPerson – a consulta externa que consome o CTE. Este exemplo usa um SELECT para consumir o CTE.
  • FROM Sales_CTE a – a referência da consulta externa ao CTE.

Além de um SELECT, também funciona com INSERT, UPDATE e DELETE. Aqui está um exemplo de uso de INSERT:
-- add a 10% increase to Employee 16 after 1 year from the previous increase.
USE AdventureWorks
GO;

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    ,eph.PayFrequency
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
)
INSERT INTO HumanResources.EmployeePayHistory
SELECT
 BusinessEntityID
,DATEADD(d,365,RateChangeDate)
,(Rate * 0.1) + Rate
,PayFrequency
,GETDATE()
FROM LatestEmployeePay

Na listagem acima, o CTE recupera o último pagamento do funcionário 16. O conjunto de resultados do CTE é usado para inserir um novo registro em EmployeePayHistory . Karl registrou suas descobertas com elegância. Além disso, ele usou exemplos apropriados.

Definir vários CTEs em 1 consulta


Está certo. Karl descobriu que vários CTEs são possíveis em uma consulta. Aqui está um exemplo:
-- Get the present and previous rate of employee 16
USE AdventureWorks
GO;

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
),
PreviousEmployeePay AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph
    INNER JOIN LatestEmployeePay lep 
      ON eph.BusinessEntityID = lep.BusinessEntityID
    WHERE eph.BusinessEntityID = 16
      AND eph.RateChangeDate < lep.RateChangeDate
    ORDER BY eph.RateChangeDate DESC
)
SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM LatestEmployeePay a
INNER JOIN PreviousEmployeePay b 
    ON a.BusinessEntityID = b.BusinessEntityID

O código acima usa 2 CTEs em uma consulta, a saber, LatestEmployeePay e AnteriorEmployeePay .

Consulte um CTE várias vezes


Há mais no exemplo anterior. Observe também que você pode INNER JOIN do primeiro CTE para o segundo CTE. Finalmente, a consulta externa pode unir os 2 CTEs. O ÚltimoEmployeePay foi referido duas vezes.

Passar argumentos para um SQL CTE


Argumentos, como variáveis, podem ser passados ​​ao longo de um CTE:
DECLARE @SalesPersonID INT = 275;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

O código acima começa declarando e definindo uma variável @SalesPersonID . O valor é então passado para o CTE para filtrar o resultado.

Usar em um CURSOR


Um cursor SQL pode usar uma instrução SELECT e percorrer os resultados. Além disso, um SQL CTE pode ser usado com ele:
DECLARE @SalesPersonID INT
DECLARE @NumberofOrders INT

DECLARE sales_cursor CURSOR FOR
    WITH Sales_CTE (SalesPersonID, NumberOfOrders)  
	AS  
	(  
		SELECT SalesPersonID, COUNT(*)  
		FROM Sales.SalesOrderHeader  
		WHERE SalesPersonID IS NOT NULL  
		GROUP BY SalesPersonID  
	)  
	SELECT salespersonid, numberoforders
	FROM Sales_CTE; 
OPEN sales_cursor
FETCH NEXT FROM sales_cursor INTO @SalesPersonID, @NumberofOrders
WHILE @@FETCH_STATUS = 0  
BEGIN
	PRINT 'SalesPersonID: ' + CAST(@SalesPersonID AS VARCHAR)
	PRINT '# of Orders: ' + CAST(@NumberofOrders AS VARCHAR)
	FETCH NEXT FROM sales_cursor  INTO @SalesPersonID, @NumberofOrders
END
CLOSE sales_cursor
DEALLOCATE sales_cursor;

Use uma tabela temporária em um CTE recursivo


O CTE recursivo usa um membro âncora e um membro recursivo dentro da definição do CTE. Ajuda a obter hierarquias dentro de uma tabela. O SQL CTE também pode usar uma tabela temporária para essa finalidade. Veja um exemplo abaixo:
-- Create a Crew table.  
CREATE TABLE #EnterpriseDSeniorOfficers  
(  
CrewID SMALLINT NOT NULL,  
FirstName NVARCHAR(30)  NOT NULL,  
LastName  NVARCHAR(40) NOT NULL,  
CrewRank NVARCHAR(50) NOT NULL,  
HigherRankID INT NULL,  
 CONSTRAINT PK_CrewID PRIMARY KEY CLUSTERED (CrewID ASC)   
);  
-- Populate the table with values.  
INSERT INTO #EnterpriseDSeniorOfficers VALUES   
 (1, N'Jean-Luc', N'Picard', N'Captain',NULL)  
,(2, N'William', N'Riker', N'First Officer',1)  
,(3, N'Data', N'', N'Second Officer',1)  
,(4, N'Worf', N'', N'Chief of Security',1)  
,(5, N'Deanna', N'Troi', N'Ship Counselor',1)  
,(6, N'Beveryly', N'Crusher', N'Chief Medical Officer',1)  
,(7, N'Geordi', N'LaForge', N'Chief Engineer',1);  

WITH DirectReports(HigherRankID, CrewID, Title, CrewLevel) AS   
(  
    SELECT HigherRankID, CrewID, CrewRank, 0 as CrewLevel
    FROM #EnterpriseDSeniorOfficers
    WHERE HigherRankID IS NULL  
    UNION ALL  
    SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
    FROM #EnterpriseDSeniorOfficers AS e  
        INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID   
)  
SELECT HigherRankID, CrewID, Title, CrewLevel   
FROM DirectReports  
OPTION (MAXRECURSION 2)
ORDER BY HigherRankID;  

DROP TABLE #EnterpriseDSeniorOfficers

Karl explicou dissecando este CTE. Aqui está como vai.

O membro âncora é a primeira instrução SELECT com o nível de tripulação zero (0):
SELECT HigherRankID, CrewID, CrewRank, 0 as CrewLevel
 FROM #EnterpriseDSeniorOfficers
 WHERE HigherRankID IS NULL

Este membro âncora obtém o nó raiz da hierarquia. A cláusula WHERE especifica que o nível raiz (HigherRankID IS NULL ).

O membro recursivo que obterá os nós filhos é extraído abaixo:
SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
FROM #EnterpriseDSeniorOfficers AS e  
INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID

Há também uma OPÇÃO (MAXRECURSION 2) usado na consulta externa. CTEs recursivos podem se tornar problemáticos quando um loop infinito resulta da consulta recursiva. MAXRECURSION 2 evita essa bagunça – limita o loop a apenas 2 recursões.

Isso encerra a lista de Karl do que funcionará. No entanto, nem tudo que pensamos pode funcionar. A próxima seção discutirá as descobertas de Karl sobre isso.

O que não funciona no SQL CTE?


Aqui, temos uma lista de coisas que irão gerar um erro ao usar o SQL CTE.

Nenhum ponto e vírgula precedendo o SQL CTE


Se houver uma instrução antes do CTE, essa instrução deve ser encerrada com um ponto e vírgula. A cláusula WITH pode funcionar para outros fins, como em uma dica de tabela, portanto, o ponto e vírgula eliminará a ambiguidade. A instrução fornecida abaixo causará um erro:
DECLARE @SalesPersonID INT

SET @SalesPersonID = 275

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

Iniciantes que costumavam não encerrar instruções com ponto e vírgula encontram este erro:

Colunas sem nome


“Esqueceu de colocar um alias de coluna? Então, você está em outro erro.” Karl disse isso em seu artigo e também forneceu um código de exemplo que compartilho abaixo:
DECLARE @SalesPersonID INT

SET @SalesPersonID = 275;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*)
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

Então, dê uma olhada na mensagem de erro:

Nomes de coluna duplicados


Outro erro relacionado ao nº 2 acima é usar o mesmo nome de coluna no CTE. Você pode se safar com uma instrução SELECT normal, mas não com um CTE. Karl deu outro exemplo:
WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID AS col1, COUNT(*) AS col1
	FROM Sales.SalesOrderHeader 
	GROUP BY SalesPersonID  
)  
SELECT *
FROM Sales_CTE

Cláusula ORDER BY sem TOP ou OFFSET-FETCH


O SQL padrão não permite ORDER BY em expressões de tabela quando o usamos para classificar os conjuntos de resultados. No entanto, se TOP ou OFFSET-FETCH for usado, ORDER BY torna-se um auxiliar de filtragem.

Aqui está o exemplo de Karl usando um ORDER BY:
WITH LatestEmployeePay
AS
(
    SELECT
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    ,eph.PayFrequency
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
)
INSERT INTO HumanResources.EmployeePayHistory
SELECT
 BusinessEntityID
,DATEADD(d,365,RateChangeDate)
,(Rate * 0.1) + Rate
,PayFrequency
,GETDATE()
FROM LatestEmployeePay

Observe que é o mesmo exemplo que tivemos anteriormente, mas desta vez, TOP não é especificado. Confira o erro:

O número de colunas não é o mesmo que a definição da lista de colunas


Karl usou o mesmo exemplo CTE recursivo, mas tirou uma coluna no membro âncora:
WITH DirectReports(HigherRankID, CrewID, Title, CrewLevel) AS   
(  
    SELECT HigherRankID, CrewID
    FROM #EnterpriseDSeniorOfficers
    WHERE HigherRankID IS NULL  
    UNION ALL  
    SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
    FROM #EnterpriseDSeniorOfficers AS e  
        INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID   
)  
SELECT HigherRankID, CrewID, Title, CrewLevel   
FROM DirectReports  
ORDER BY HigherRankID;

O código acima usa uma lista de três colunas com um formulário externo, mas o membro âncora tinha apenas 2 colunas. Não é permitido. Cometer um erro como este acionará um erro:

Outras coisas não permitidas em um CTE


Além da lista acima, aqui estão mais algumas das descobertas de Karl que acionam erros se você usá-lo por engano em um SQL CTE.
  • Usando a cláusula SELECT INTO, OPTION com dicas de consulta e usando FOR BROWSE.
  • Dados e tipos diferentes nas colunas do membro âncora em comparação com as colunas do membro recursivo.
  • Ter as seguintes palavras-chave em um membro recursivo de uma CTE recursiva:
    • PARTE SUPERIOR
    • OUTER JOIN (mas INNER JOIN é permitido)
    • GRUPO POR e HAVING
    • Subconsultas
    • SELECIONAR DISTINTO
  • Usando agregação escalar.
  • Usando subconsultas em um membro recursivo.
  • Ter CTEs SQL aninhadas.

SQL CTE x Tabelas temporárias x Subconsultas


Às vezes, você pode reescrever um SQL CTE usando uma subconsulta. Além disso, às vezes, você pode dividir um SQL CTE usando tabelas temporárias por motivos de desempenho. Como qualquer outra consulta, você precisa verificar o Plano de Execução Real e STATISTICS IO para saber qual opção tomar. SQL CTE pode ser amigável aos olhos, mas se você atingir uma barreira de desempenho, use outra opção . Nenhuma opção é mais rápida que a outra.

Vamos examinar três consultas dos artigos de Karl que trazem os mesmos resultados. Um usa um SQL CTE, o outro usa uma subconsulta e o terceiro usa uma tabela temporária. Para simplificar, Karl usou um pequeno conjunto de resultados.

O código e o ResultSet


Começou usando vários CTEs SQL.
WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
),
PreviousEmployeePay AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph
    INNER JOIN LatestEmployeePay lep 
        ON eph.BusinessEntityID = lep.BusinessEntityID
    WHERE eph.BusinessEntityID = 16
        AND eph.RateChangeDate < lep.RateChangeDate
    ORDER BY eph.RateChangeDate DESC
)
SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM LatestEmployeePay a
INNER JOIN PreviousEmployeePay b 
    ON a.BusinessEntityID = b.BusinessEntityID

A próxima é uma subconsulta. Como você percebe, o CTE parece modular e legível, mas a subconsulta abaixo é mais curta:
SELECT TOP 1
 eph.BusinessEntityID
,eph.Rate
,eph.RateChangeDate
,(SELECT TOP 1 eph1.Rate FROM HumanResources.EmployeePayHistory eph1
  WHERE eph1.BusinessEntityID=16
    AND eph1.RateChangeDate < eph.RateChangeDate
  ORDER BY eph1.RateChangeDate DESC) AS PreviousRate
FROM HumanResources.EmployeePayHistory eph
WHERE eph.BusinessEntityID = 16
ORDER BY eph.RateChangeDate DESC;

Karl também tentou dividi-lo em pequenos pedaços de código e depois combinar os resultados usando tabelas temporárias.
SELECT TOP 1
 eph.BusinessEntityID
,eph.RateChangeDate
,eph.Rate
INTO #LatestPay
FROM HumanResources.EmployeePayHistory eph 
WHERE eph.BusinessEntityID = 16
ORDER BY eph.RateChangeDate DESC

SELECT TOP 1
 eph.BusinessEntityID
,eph.RateChangeDate
,eph.Rate
INTO #PreviousPay
FROM HumanResources.EmployeePayHistory eph
INNER JOIN #LatestPay lep 
    ON eph.BusinessEntityID = lep.BusinessEntityID
WHERE eph.BusinessEntityID = 16
    AND eph.RateChangeDate < lep.RateChangeDate
ORDER BY eph.RateChangeDate DESC

SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM #LatestPay a
INNER JOIN #PreviousPay b 
    ON a.BusinessEntityID = b.BusinessEntityID

DROP TABLE #LatestPay
DROP TABLE #PreviousPay

Dê uma olhada nestes resultados de três maneiras de obter os pagamentos atuais e anteriores para o Funcionário 16. Eles são os mesmos:

As leituras lógicas


O que consome mais recursos do SQL Server? Vejamos as ESTATÍSTICAS IO. Karl usou o statisticsparser.com para formatar bem os resultados – bom para nós.

A Figura 7 mostra leituras lógicas do uso de um CTE versus o uso de uma subconsulta:

Em seguida, veja o total de leituras lógicas ao dividir o código em pequenos pedaços usando tabelas temporárias:

Então, o que consome mais recursos? Karl os classificou para maior clareza.
  1. Subconsulta – 4 leituras lógicas (VENCEDOR!).
  2. SQL CTE – 6 leituras lógicas.
  3. Tabelas temporárias – 8 leituras lógicas.

Neste exemplo, o mais rápido será a subconsulta.

O Plano de Execução Real


Para entender as leituras lógicas que obtivemos do STATISTICS IO, Karl também verificou o Plano de Execução Real. Começou no CTE:

Podemos observar algumas coisas desse plano:
  • ÚltimosEmployeePay A CTE foi avaliada duas vezes quando usada na consulta externa e quando associada a PreviousEmployeePay . Então, vemos 2 nós TOP para isso.
  • Nós vemos AnteriorEmployeePay avaliado uma vez.

Em seguida, observe o Plano de Execução Real da consulta com uma subconsulta:

Há algumas coisas óbvias aqui:
  • O plano é mais simples.
  • É mais simples porque a subconsulta para obter o último pagamento é avaliada apenas uma vez.
  • Não é à toa que as leituras lógicas são menores em comparação com as leituras lógicas da consulta com um CTE.

Por fim, aqui está o Plano de Execução Real quando Karl usou tabelas temporárias:

Como é um lote de três instruções, também vemos três diagramas no plano. Todos os três são simples, mas o plano coletivo não é tão simples quanto o plano da consulta com a subconsulta.

As estatísticas de tempo


Usando a solução dbForge Studio for SQL Server, você pode comparar as estatísticas de tempo no Query Profiler. Para isso, mantenha pressionada a tecla CTRL e clique nos nomes dos resultados de cada consulta no histórico de execução:

As estatísticas de tempo são consistentes com as leituras lógicas e o Plano de Execução Real. A subconsulta foi a mais rápida (88ms). Isso é seguido pelo CTE (199ms). A última é o uso de tabelas temporárias (536ms).

Então, o que aprendemos com Karl?

Neste exemplo em particular, vimos que usar uma subconsulta é muito melhor quando queremos a opção mais rápida. No entanto, pode ser uma história diferente se o conjunto de requisitos não for tal.

Sempre verifique as ESTATÍSTICAS IO e os Planos de Execução Reais para saber qual técnica usar.

Recomendações


Espero que você não tenha batido a cabeça na parede para entender o que é um CTE (Common Table Expressions). Caso contrário, você pode obter um CTE (encefalopatia traumática crônica). Brincadeiras à parte, o que revelamos?
  • As expressões de tabela comuns são conjuntos de resultados nomeados temporariamente. É mais próximo de uma subconsulta em comportamento e escopo, mas mais claro e modular. Ele também tem um nome.
  • SQL CTE é para simplificar o código, não para tornar sua consulta mais rápida.
  • Sete coisas que aprendemos funcionarão no SQL CTE.
  • Cinco coisas acionarão um erro.
  • Também confirmamos mais uma vez que o IO de ESTATÍSTICAS e o Plano de Execução Real sempre fornecerão um julgamento melhor. É verdade se você comparar um CTE com uma subconsulta e um lote usando tabelas temporárias.

Gostou? Em seguida, os botões de mídia social estão esperando para serem pressionados. Escolha o seu favorito e compartilhe o amor!

Leia também


Como o CTE pode ajudar na escrita de consultas complexas e poderosas:uma perspectiva de desempenho