Database
 sql >> Database >  >> RDS >> Database

Tutto ciò che devi sapere su SQL CTE in un unico punto

La prima volta che Karl ha sentito parlare di SQL Server CTE è stato quando stava cercando qualcosa che rendesse il suo codice SQL più facile per gli occhi. È una specie di mal di testa quando lo guardi. Anton, il suo collega preoccupato, gli ha chiesto del CTE. Karl pensava che Anton si riferisse al suo mal di testa. Forse ha sentito tutto male, quindi ha risposto:"Certo che no". La cosa divertente è che si riferiva all'encefalopatia traumatica cronica, anch'essa un CTE, una malattia neurodegenerativa causata da ripetute ferite alla testa. Ma sulla base della risposta di Karl, Anton sapeva per certo che il suo collega era all'oscuro di quello che stava dicendo.

Che modo pazzesco di introdurre i CTE! Quindi, prima di entrare nella stessa barca, chiariamo, cos'è SQL CTE o Common Table Expressions nel mondo SQL?

Puoi leggere le basi qui. Nel frattempo, scopriremo qualcosa in più su cosa è successo in questa storia insolita.

4 Informazioni di base sul CTE in SQL Server

"Un CTE SQL ha un nome"

Anton ha iniziato con l'idea che i CTE SQL fossero temporaneamente denominati set di risultati. Essendo un mezzo temporaneo, CTE ha una portata limitata.

"Quindi, è come una sottoquery?" chiese Carlo.

«In un certo senso sì. Ma non puoi nominare una sottoquery", ha detto Anton. “Un CTE ha un nome molto simile a una tabella con un nome. Tuttavia, invece di CREATE, usi WITH per crearlo. Quindi, ha scritto la sintassi su carta:

WITH <cte_name>(<column list>)
AS
(
<inner query defining the CTE>
)
<outer query against CTE>

"CTE's Gone When the SELECT è fatto"

Anton ha continuato spiegando l'ambito di SQL CTE.

"Una tabella temporanea può esistere nell'ambito della procedura o globalmente. Ma CTE è sparito quando il SELECT è finito", ha detto con una rima. "Stessa cosa se lo usi per INSERT, UPDATE o DELETE", ha continuato.

"Non puoi riutilizzarlo"

"A differenza di una vista o di una tabella temporanea, non è possibile riutilizzare SQL CTE. Il nome è lì, quindi puoi fare riferimento ad esso all'interno della query interna ed esterna. Ma questo è tutto", ha detto Anton.

"Quindi, qual è il problema delle CTE SQL?" chiese Carlo.

"Puoi rendere il tuo codice più leggibile"

"Il grosso problema?" Anton ha risposto alla domanda. “È che puoi rendere il tuo codice facilmente leggibile. Non è quello che stai cercando?"

"Esatto", ammise Karl.

Allora, qual è il prossimo passo logico da fare per Karl?

Cose extra su CTE in SQL

Il giorno successivo, Karl ha continuato la sua ricerca per SQL CTE. A parte quanto sopra, ecco cosa ha trovato:

  • SQL CTE può essere non ricorsivo o ricorsivo.
  • Non solo SQL Server, ma anche MySQL e Oracle supportano l'idea. In effetti, fa parte delle specifiche SQL-99.
  • Sebbene venga utilizzato per semplificare il codice SQL, non migliora le prestazioni.
  • Inoltre non sostituirà le sottoquery e le tabelle temporanee. Ognuno ha il suo posto e il suo utilizzo.

In breve, è un altro modo per esprimere una query .

Ma Karl era affamato di maggiori dettagli, quindi ha continuato a cercare cosa avrebbe funzionato, cosa non avrebbe funzionato e come si sarebbe comportato rispetto alle sottoquery e alle tabelle temporanee.

Cosa funzionerà in SQL Server CTE?

Scavando ulteriormente per svelare di più su CTE, Karl ha elencato di seguito ciò che SQL Server avrebbe accettato. Dai un'occhiata anche ai suoi studi.

Assegna alias di colonna in linea o esterni

I CTE SQL supportano due forme di assegnazione di alias di colonna. Il primo è il modulo inline, come nell'esempio seguente:

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

Il codice precedente utilizza l'alias di colonna all'interno della definizione CTE quando viene assegnato all'interno dell'istruzione SELECT. Hai notato COUNT(*) AS NumberOfOrders ? Questo è il modulo in linea.

Ora, un altro esempio è la forma esterna:

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

Le colonne possono anche essere definite tra parentesi dopo aver impostato il nome CTE. Nota il WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE in SQL precede SELECT, INSERT, UPDATE o DELETE

Il prossimo punto riguarda il consumo del CTE. Il primo e comune esempio è quando precede un'istruzione 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

Cosa mostra questo esempio?

  • CTE_vendita – il nome del CTE.
  • (ID persona di vendita, numero di ordini) – la definizione delle colonne CTE.
  • SELECT SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader WHERE SalesPersonID NON È NULL GROUP BY SalesPersonID – il SELECT interno che definisce il CTE.
  • SELECT a.SalesPersonID, CONCAT(P.LastName,', ',P.FirstName,' ',P.MiddleName) AS SalesPerson – la query esterna che consuma il CTE. Questo esempio utilizza un SELECT per consumare il CTE.
  • DA Sales_CTE a – il riferimento della query esterna al CTE.

Oltre a SELECT, funziona anche con INSERT, UPDATE e DELETE. Ecco un esempio di utilizzo di 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

Nell'elenco sopra, il CTE recupera l'ultima retribuzione per il dipendente 16. Il set di risultati del CTE viene quindi utilizzato per inserire un nuovo record in EmployeePayHistory . Karl ha registrato le sue scoperte con eleganza. Inoltre, ha usato esempi appropriati.

Definisci più CTE in una query

Giusto. Karl ha scoperto che più CTE sono possibili in 1 query. Ecco un esempio:

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

Il codice precedente utilizza 2 CTE in una query, ovvero LatestEmployeePay e PreviousEmployeePay .

Fai riferimento a un CTE più volte

C'è di più nell'esempio precedente. Nota anche che puoi INNER JOIN dal primo CTE al secondo CTE. Infine, la query esterna può unire entrambi i 2 CTE. LatestEmployeePay è stato citato due volte.

Trasferisci argomenti a un CTE SQL

Argomenti, come variabili, possono essere passati lungo un 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

Il codice precedente inizia dichiarando e impostando una variabile @SalesPersonID . Il valore viene quindi passato al CTE per filtrare il risultato.

Utilizzare in un CURSORE

Un cursore SQL può utilizzare un'istruzione SELECT e scorrere i risultati. Inoltre, è possibile utilizzare un CTE SQL con esso:

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;

Utilizzare una tabella temporanea in un CTE ricorsivo

CTE ricorsivo utilizza un membro di ancoraggio e un membro ricorsivo all'interno della definizione CTE. Aiuta a ottenere gerarchie all'interno di una tabella. SQL CTE può anche utilizzare una tabella temporanea per questo scopo. Vedi un esempio qui sotto:

-- 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 ha spiegato sezionando questo CTE. Ecco come va.

Il membro di ancoraggio è la prima istruzione SELECT con il livello di equipaggio zero (0):

SELECT HigherRankID, CrewID, CrewRank, 0 as CrewLevel
 FROM #EnterpriseDSeniorOfficers
 WHERE HigherRankID IS NULL

Questo membro di ancoraggio ottiene il nodo radice della gerarchia. La clausola WHERE specifica che il livello radice (HigherRankID IS NULL ).

Il membro ricorsivo che otterrà i nodi figlio viene estratto di seguito:

SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
FROM #EnterpriseDSeniorOfficers AS e  
INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID

C'è anche un'OPZIONE (MAXRECURSION 2) utilizzato nella query esterna. I CTE ricorsivi possono diventare problematici quando un ciclo infinito risulta dalla query ricorsiva. MAXRECURSION 2 evita questo pasticcio:limita il ciclo a 2 sole ricorsioni.

Questo conclude l'elenco di Karl di ciò che funzionerà. Tuttavia, non tutto ciò a cui pensiamo potrebbe funzionare. La prossima sezione discuterà le scoperte di Karl su questi.

Cosa non funzionerà in SQL CTE?

Qui abbiamo un elenco di cose che genereranno un errore quando si utilizza SQL CTE.

Nessun punto e virgola che precede il CTE SQL

Se è presente un'istruzione prima del CTE, tale istruzione deve essere terminata con un punto e virgola. La clausola WITH può funzionare per altri scopi come in un suggerimento di tabella, quindi il punto e virgola eliminerà l'ambiguità. L'istruzione fornita di seguito causerà un errore:

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

I principianti che non terminavano le istruzioni con un punto e virgola riscontrano questo errore:

Colonne senza nome

“Dimenticato di inserire un alias di colonna? Quindi, sei in un altro errore. " Karl ha detto questo nel suo articolo e ha anche fornito un codice di esempio che condivido di seguito:

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

Quindi, dai un'occhiata al messaggio di errore:

Nomi colonne duplicati

Un altro errore relativo al n. 2 sopra è l'utilizzo dello stesso nome di colonna all'interno del CTE. Puoi farla franca in una normale istruzione SELECT, ma non con un CTE. Karl ha avuto un altro esempio:

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID AS col1, COUNT(*) AS col1
	FROM Sales.SalesOrderHeader 
	GROUP BY SalesPersonID  
)  
SELECT *
FROM Sales_CTE

Clausola ORDER BY Senza TOP o OFFSET-FETCH

L'SQL standard non consente ORDER BY nelle espressioni di tabella quando lo utilizziamo per ordinare i set di risultati. Tuttavia, se si utilizza TOP o OFFSET-FETCH, ORDER BY diventa un ausilio di filtraggio.

Ecco l'esempio di Karl che utilizza un 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

Si noti che è lo stesso esempio che avevamo in precedenza, ma questa volta TOP non è specificato. Controlla l'errore:

Il numero di colonne non corrisponde alla definizione dell'elenco di colonne

Karl ha utilizzato lo stesso esempio CTE ricorsivo, ma ha eliminato una colonna nel membro anchor:

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;

Il codice precedente utilizza un elenco di tre colonne con un modulo esterno, ma il membro anchor aveva solo 2 colonne. Non è permesso. Fare un errore come questo attiverà un errore:

Altre cose non consentite in un CTE

A parte l'elenco sopra, ecco alcuni altri risultati di Karl che provocano errori se lo usi per errore in un CTE SQL.

  • Utilizzo di SELECT INTO, clausola OPTION con suggerimenti per le query e utilizzo di FOR BROWSE.
  • Dati e tipi diversi nelle colonne dei membri di ancoraggio rispetto alle colonne dei membri ricorsivi.
  • Avere le seguenti parole chiave in un membro ricorsivo di un CTE ricorsivo:
    • TOP
    • OUTER JOIN (ma è consentito INNER JOIN)
    • GRUPPO PER e AVERE
    • Subquery
    • SELEZIONA DISTINTO
  • Utilizzo dell'aggregazione scalare.
  • Utilizzo di sottoquery in un membro ricorsivo.
  • Avere CTE SQL nidificati.

SQL CTE rispetto a tabelle temporanee e sottoquery

A volte, puoi riscrivere un CTE SQL utilizzando una sottoquery. Inoltre, a volte, puoi scomporre un CTE SQL utilizzando tabelle temporanee per motivi di prestazioni. Come qualsiasi altra query, è necessario controllare il piano di esecuzione effettivo e STATISTICS IO per sapere quale opzione scegliere. SQL CTE può essere piacevole per gli occhi, ma se colpisci un muro delle prestazioni, usa un'altra opzione . Nessuna opzione è più veloce dell'altra.

Esaminiamo tre domande dagli articoli di Karl che portano gli stessi risultati. Uno usa un CTE SQL, l'altro usa una sottoquery e il terzo usa una tabella temporanea. Per semplicità, Karl ha utilizzato un piccolo set di risultati.

Il codice e il set di risultati

È iniziato utilizzando più CTE 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

Il prossimo è una sottoquery. Come puoi notare, il CTE sembra modulare e leggibile, ma la sottoquery seguente è più breve:

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 ha anche provato a dividerlo in piccoli blocchi di codice e quindi a combinare i risultati utilizzando tabelle temporanee.

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

Dai un'occhiata a questi tre modi per ottenere la retribuzione attuale e precedente per il Dipendente 16. Sono gli stessi:

Le letture logiche

Cosa consuma la maggior parte delle risorse di SQL Server? Diamo un'occhiata a STATISTICHE IO. Karl ha utilizzato statisticheparser.com per formattare bene i risultati:buon per noi.

La figura 7 mostra le letture logiche dell'utilizzo di un CTE rispetto all'utilizzo di una sottoquery:

Quindi, guarda le letture logiche totali quando suddividi il codice in piccoli blocchi utilizzando tabelle temporanee:

Quindi, cosa consuma più risorse? Karl li ha classificati per chiarezza.

  1. Subquery – 4 letture logiche (VINCITORE!).
  2. SQL CTE – 6 letture logiche.
  3. Tabelle temporanee:8 letture logiche.

In questo esempio, la più veloce sarà la subquery.

Il piano di esecuzione effettivo

Per dare un senso alle letture logiche che abbiamo ottenuto da STATISTICS IO, Karl ha anche controllato l'Actual Execution Plan. È partito dal CTE:

Possiamo osservare alcune cose da questo piano:

  • LatestEmployeePay CTE è stato valutato due volte quando utilizzato nella query esterna e quando è unito a PreviousEmployeePay . Quindi, vediamo 2 nodi TOP per questo.
  • Vediamo PreviousEmployeePay valutato una volta.

Quindi, guarda il piano di esecuzione effettivo della query con una sottoquery:

Ci sono alcune cose ovvie qui:

  • Il piano è più semplice.
  • È più semplice perché la sottoquery per ottenere l'ultimo stipendio viene valutata una sola volta.
  • Non c'è da stupirsi che le letture logiche siano inferiori rispetto alle letture logiche della query con un CTE.

Infine, ecco il piano di esecuzione effettivo quando Karl utilizzava le tabelle temporanee:

Poiché si tratta di un lotto di tre affermazioni, nel piano vediamo anche tre diagrammi. Tutti e tre sono semplici, ma il piano collettivo non è semplice come il piano della query con la sottoquery.

Le statistiche temporali

Utilizzando la soluzione dbForge Studio per SQL Server, puoi confrontare le statistiche temporali in Query Profiler. Per questo, tieni premuto il tasto CTRL e fai clic sui nomi dei risultati di ciascuna query nella cronologia di esecuzione:

Le statistiche temporali sono coerenti con le letture logiche e il piano di esecuzione effettivo. La sottoquery è stata la più veloce (88 ms). Segue il CTE (199ms). L'ultimo è l'uso di tabelle temporanee (536 ms).

Allora, cosa abbiamo imparato da Karl?

In questo esempio particolare, abbiamo visto che l'uso di una sottoquery è molto meglio quando vogliamo l'opzione più veloce. Tuttavia, può essere una storia diversa se l'insieme dei requisiti non è tale.

Controlla sempre STATISTICS IO e Actual Execution Plans per sapere quale tecnica utilizzare.

Da asporto

Spero che tu non abbia sbattuto la testa al muro per capire cos'è un CTE (Common Table Expressions). In caso contrario, potresti avere un CTE (Cronic Traumatic Encephalopathy). Scherzi a parte, cosa abbiamo rivelato?

  • Le espressioni di tabella comuni sono set di risultati temporaneamente denominati. È più vicino a una sottoquery in termini di comportamento e ambito, ma più chiaro e modulare. Ha anche un nome.
  • SQL CTE serve per semplificare il codice, non per rendere più veloci le tue query.
  • Sette cose che abbiamo imparato funzioneranno su SQL CTE.
  • Cinque cose attiveranno un errore.
  • Abbiamo anche confermato ancora una volta che STATISTICS IO e Actual Execution Plan ti daranno sempre un giudizio migliore. È vero se si confronta un CTE con una sottoquery e un batch utilizzando tabelle temporanee.

Piaciuto? Quindi i pulsanti dei social media aspettano di essere premuti. Scegli il tuo preferito e condividi l'amore!

Leggi anche

In che modo CTE può aiutare a scrivere query complesse e potenti:una prospettiva di performance