sql >> Database >  >> RDS >> Database

Alles wat u moet weten over SQL CTE op één plek

De eerste keer dat Karl van SQL Server CTE hoorde, was toen hij op zoek was naar iets om zijn SQL-code gemakkelijker te maken voor het oog. Het is een soort hoofdpijn als je ernaar kijkt. Anton, zijn bezorgde collega, vroeg hem naar CTE. Karl dacht dat Anton zijn hoofdpijn bedoelde. Misschien hoorde hij het helemaal verkeerd, dus antwoordde hij:"Natuurlijk niet." Het grappige is dat hij verwees naar chronische traumatische encefalopathie, ook een CTE - een neurodegeneratieve ziekte die wordt veroorzaakt door herhaalde hoofdletsels. Maar op basis van Karls reactie wist Anton zeker dat zijn collega geen idee had wat hij zei.

Wat een gekke manier om CTE's te introduceren! Laten we, voordat u in hetzelfde schuitje stapt, eerst verduidelijken:wat is SQL CTE of Common Table Expressions in de SQL-wereld?

De basis kun je hier lezen. Ondertussen leren we wat meer over wat er is gebeurd in dit ongewone verhaal.

4 basisdingen over CTE in SQL Server

"Een SQL CTE heeft een naam"

Anton begon met het idee dat SQL CTE's tijdelijk resultatensets heten. Omdat het tijdelijk is, is CTE beperkt in reikwijdte.

"Dus het is net een subquery?" vroeg Karl.

“In zekere zin wel. Maar je kunt geen subquery noemen, 'zei Anton. “Een CTE heeft een naam die veel lijkt op een tabel met een naam. In plaats van CREATE gebruik je WITH om het te maken.” Daarna schreef hij de syntaxis op papier:

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

"CTE's Gone When the SELECT is Done"

Anton ging verder met het uitleggen van de reikwijdte van SQL CTE.

“Een tijdelijke tabel kan bestaan ​​binnen de reikwijdte van de procedure of globaal. Maar CTE is weg als de SELECT gedaan is", zei hij met rijm. "Hetzelfde als je het gebruikt voor INSERT, UPDATE of DELETE," vervolgde hij.

"Je kunt het niet opnieuw gebruiken"

“In tegenstelling tot een weergave of een tijdelijke tabel, kun je SQL CTE niet hergebruiken. De naam is er, dus je kunt ernaar verwijzen in de innerlijke en de uiterlijke vraag. Maar dat is alles,” vertelde Anton.

"Dus, wat is er zo belangrijk aan SQL CTE's?" vroeg Karl.

"U kunt uw code leesbaarder maken"

"Het grote probleem?" Anton beantwoordde de vraag. “Het is dat je je code gemakkelijk leesbaar kunt maken. Is dat niet wat u zoekt?”

"Dat klopt," gaf Karl toe.

Dus, wat is de volgende logische stap voor Karl om te doen?

Extra dingen over CTE in SQL

De volgende dag vervolgde Karl zijn zoektocht naar SQL CTE. Afgezien van het bovenstaande, is dit wat hij vond:

  • SQL CTE kan niet-recursief of recursief zijn.
  • Niet alleen SQL Server maar ook MySQL en Oracle ondersteunen het idee. Het is in feite een onderdeel van de SQL-99-specificaties.
  • Hoewel het wordt gebruikt om SQL-code te vereenvoudigen, verbetert het de prestaties niet.
  • En het vervangt ook geen subquery's en tijdelijke tabellen. Elk heeft zijn plaats en gebruik.

Kortom, het is een andere manier om een ​​vraag te uiten .

Maar Karl was hongerig naar meer details, dus bleef hij zoeken naar wat wel en niet zou werken en hoe het zou presteren in vergelijking met subquery's en tijdelijke tabellen.

Wat werkt in SQL Server CTE?

Verder gravend om meer over CTE te ontrafelen, vermeldde Karl hieronder wat SQL Server zou accepteren. Kijk ook eens naar zijn studie.

Inline of externe kolomaliassen toewijzen

SQL CTE's ondersteunen twee vormen van het toewijzen van kolomaliassen. De eerste is het inline-formulier, zoals in het onderstaande voorbeeld:

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

De bovenstaande code gebruikt een kolomalias binnen de CTE-definitie wanneer deze wordt toegewezen in de SELECT-instructie. Heb je de COUNT(*) AS NumberOfOrders . opgemerkt ? Dat is het inline-formulier.

Een ander voorbeeld is de externe vorm:

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

De kolommen kunnen ook tussen haakjes worden gedefinieerd na het instellen van de CTE-naam. Let op de WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE in SQL gaat vooraf aan een SELECT, INSERT, UPDATE of DELETE

Dit volgende item gaat over het consumeren van de CTE. Het eerste en veelvoorkomende voorbeeld is wanneer het voorafgaat aan een SELECT-instructie.

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

Wat laat dit voorbeeld zien?

  • Sales_CTE – de naam van de CTE.
  • (SalesPersonID, NumberOfOrders) – de definitie van CTE-kolommen.
  • SELECTEER SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader WAAR SalesPersonID NIET NULL IS GROEP DOOR SalesPersonID – de innerlijke SELECT die de CTE definieert.
  • SELECTEER a.SalesPersonID, CONCAT(P.LastName,', ',P.FirstName,' ',P.MiddleName) AS SalesPerson – de buitenste query die de CTE verbruikt. In dit voorbeeld wordt een SELECT gebruikt om de CTE te gebruiken.
  • VAN Sales_CTE een – de verwijzing van de buitenste zoekopdracht naar de CTE.

Naast een SELECT werkt het ook met INSERT, UPDATE en DELETE. Hier is een voorbeeld van het gebruik van 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

In de bovenstaande lijst haalt de CTE het laatste loon voor werknemer 16 op. De resultatenset van de CTE wordt vervolgens gebruikt om een ​​nieuw record in te voegen in EmployeePayHistory . Karl legde zijn bevindingen elegant vast. Ook gebruikte hij passende voorbeelden.

Definieer meerdere CTE's in 1 zoekopdracht

Dat klopt. Karl ontdekte dat er meerdere CTE's mogelijk zijn in 1 zoekopdracht. Hier is een voorbeeld:

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

De bovenstaande code gebruikt 2 CTE's in één zoekopdracht, namelijk LatestEmployeePay en VorigeEmployeePay .

Verwijs meerdere keren naar een CTE

Er is meer aan het vorige voorbeeld. Merk ook op dat u INNERLIJK de eerste CTE kunt toevoegen aan de tweede CTE. Ten slotte kan de buitenste query zich bij beide CTE's voegen. De LaatsteEmployeePay er is twee keer naar verwezen.

Argumenten doorgeven aan een SQL CTE

Argumenten, zoals variabelen, kunnen langs een CTE worden doorgegeven:

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

De bovenstaande code begint met het declareren en instellen van een variabele @SalesPersonID . De waarde wordt vervolgens doorgegeven aan de CTE om het resultaat te filteren.

Gebruik in een CURSOR

Een SQL-cursor kan een SELECT-instructie gebruiken en door de resultaten lopen. Er kan ook een SQL CTE mee worden gebruikt:

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;

Gebruik een tijdelijke tabel in een recursieve CTE

Recursieve CTE gebruikt een ankerlid en een recursief lid binnen de CTE-definitie. Het helpt om hiërarchieën binnen een tabel te krijgen. SQL CTE kan hiervoor ook een tijdelijke tabel gebruiken. Zie hieronder een voorbeeld:

-- 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 legde uit door deze CTE te ontleden. Zo gaat het.

Het ankerlid is de eerste SELECT-instructie met het bemanningsniveau nul (0):

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

Dit ankerlid krijgt het hoofdknooppunt van de hiërarchie. De WHERE-component specificeert dat het hoofdniveau (HigherRankID IS NULL ).

Het recursieve lid dat de onderliggende knooppunten zal krijgen, wordt hieronder geëxtraheerd:

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

Er is ook een OPTIE (MAXRECURSION 2) gebruikt in de buitenste query. Recursieve CTE's kunnen problematisch worden wanneer een oneindige lus het resultaat is van de recursieve query. MAXRECURSION 2 vermijdt deze puinhoop – het beperkt de lus tot slechts 2 recursies.

Hiermee eindigt Karl's lijst van wat zal werken. Niet alles waar we aan denken, kan echter werken. In het volgende gedeelte worden Karls bevindingen hierover besproken.

Wat werkt niet in SQL CTE?

Hier hebben we een lijst met dingen die een fout zullen genereren bij het gebruik van SQL CTE.

Geen puntkomma voorafgaand aan de SQL CTE

Als er een instructie vóór de CTE staat, moet die instructie worden afgesloten met een puntkomma. De WITH-component kan voor andere doeleinden werken, zoals in een tabelhint, dus de puntkomma zal dubbelzinnigheid wegnemen. De onderstaande verklaring zal een fout veroorzaken:

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

First-timers die instructies niet met een puntkomma beëindigden, komen deze fout tegen:

Naamloze kolommen

“Vergeten een kolomalias te plaatsen? Dan wacht je nog een fout.” Karl zei dit in zijn paper en gaf ook een voorbeeldcode die ik hieronder deel:

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

Bekijk dan de foutmelding:

Dubbele kolomnamen

Een andere fout met betrekking tot #2 hierboven is het gebruik van dezelfde kolomnaam binnen de CTE. Je kunt ermee wegkomen in een normale SELECT-instructie, maar niet met een CTE. Karl had nog een voorbeeld:

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

ORDER PER clausule zonder TOP of OFFSET-FETCH

Standaard SQL staat ORDER BY niet toe in tabelexpressies wanneer we het gebruiken voor het sorteren van de resultaatsets. Als echter TOP of OFFSET-FETCH wordt gebruikt, wordt ORDER BY een filterhulpmiddel.

Hier is Karls voorbeeld met een 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

Merk op dat het hetzelfde voorbeeld is dat we eerder hadden, maar deze keer is TOP niet gespecificeerd. Bekijk de fout:

Het aantal kolommen is niet hetzelfde als de kolomlijstdefinitie

Karl gebruikte hetzelfde recursieve CTE-voorbeeld, maar hij haalde een kolom uit het ankerlid:

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;

De bovenstaande code gebruikt een lijst met drie kolommen met een externe vorm, maar het ankerlid had slechts 2 kolommen. Het is niet toegestaan. Als u een dergelijke fout maakt, wordt er een fout gegenereerd:

Andere dingen die niet zijn toegestaan ​​in een CTE

Afgezien van de bovenstaande lijst, zijn hier nog een paar bevindingen van Karl die fouten veroorzaken als je het per ongeluk gebruikt in een SQL CTE.

  • Gebruik SELECT INTO, OPTION-clausule met zoekopdrachthints en gebruik FOR BROWSE.
  • Verschillende gegevens en typen in de kolommen met ankerleden vergeleken met de kolommen met recursieve leden.
  • De volgende trefwoorden hebben in een recursief lid van een recursieve CTE:
    • TOP
    • OUTER JOIN (Maar INNER JOIN is toegestaan)
    • GROEPEREN OP EN METEN
    • Subquery's
    • SELECTEER DISTINCT
  • Scalaire aggregatie gebruiken.
  • Subquery's gebruiken in een recursief lid.
  • SQL CTE's genest.

SQL CTE versus tijdelijke tabellen versus subquery's

Soms kunt u een SQL CTE herschrijven met behulp van een subquery. Soms kunt u om prestatieredenen ook een SQL CTE opsplitsen met behulp van tijdelijke tabellen. Net als bij elke andere vraag, moet u het Actual Execution Plan en STATISTICS IO controleren om te weten welke optie u moet nemen. SQL CTE is misschien vriendelijk voor de ogen, maar als je een prestatiemuur raakt, gebruik dan een andere optie . Geen enkele optie is sneller dan de andere.

Laten we eens kijken naar drie vragen uit Karls papieren die dezelfde resultaten opleveren. De ene gebruikt een SQL CTE, de andere gebruikt een subquery en de derde gebruikt een tijdelijke tabel. Voor de eenvoud gebruikte Karl een kleine resultatenset.

De Code en ResultSet

Het begon met het gebruik van meerdere SQL CTE's.

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

De volgende is een subquery. Zoals je merkt, ziet de CTE er modulair en leesbaar uit, maar de onderstaande subquery is korter:

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 probeerde het ook in kleine stukjes code te verdelen en de resultaten vervolgens te combineren met behulp van tijdelijke tabellen.

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

Bekijk deze drie manieren om het huidige en eerdere loon voor werknemer 16 te krijgen. Ze zijn hetzelfde:

De logische leest

Wat verbruikt de meeste SQL Server-bronnen? Laten we eens kijken naar de STATISTICS IO. Karl gebruikte statistiekenparser.com om de resultaten netjes op te maken - goed voor ons.

Afbeelding 7 toont logische uitlezingen van het gebruik van een CTE versus het gebruik van een subquery:

Bekijk vervolgens de totale logische uitlezingen bij het opsplitsen van de code in kleine stukjes met behulp van tijdelijke tabellen:

Dus, wat verbruikt meer middelen? Karl rangschikte ze voor de duidelijkheid.

  1. Subquery – 4 logische reads (WINNAAR!).
  2. SQL CTE – 6 logische reads.
  3. Tijdstabellen – 8 logische uitlezingen.

In dit voorbeeld is de subquery de snelste.

Het daadwerkelijke uitvoeringsplan

Om de logische resultaten te begrijpen die we van de STATISTICS IO kregen, controleerde Karl ook het werkelijke uitvoeringsplan. Hij begon bij de CTE:

We kunnen een paar dingen opmerken uit dit plan:

  • LaatsteEmployeePay CTE is twee keer geëvalueerd bij gebruik in de buitenste zoekopdracht en wanneer deze wordt samengevoegd met PreviousEmployeePay . We zien hiervoor dus 2 TOP-knooppunten.
  • We zien PreviousEmployeePay eenmaal geëvalueerd.

Bekijk vervolgens het werkelijke uitvoeringsplan van de query met een subquery:

Er zijn een paar voor de hand liggende dingen:

  • Het plan is eenvoudiger.
  • Het is eenvoudiger omdat de subquery om het laatste loon te krijgen slechts één keer wordt geëvalueerd.
  • Geen wonder dat de logische uitlezingen minder zijn in vergelijking met de logische uitlezingen van de query met een CTE.

Ten slotte is hier het werkelijke uitvoeringsplan toen Karl tijdelijke tabellen gebruikte:

Omdat het een batch van drie stellingen is, zien we ook drie diagrammen in het plan. Alle drie zijn eenvoudig, maar het collectieve plan is niet zo eenvoudig als het plan van de query met de subquery.

De tijdstatistieken

Met behulp van de dbForge Studio for SQL Server-oplossing kunt u de tijdstatistieken in Query Profiler vergelijken. Houd daarvoor de CTRL-toets ingedrukt en klik op de resultaatnamen van elke query in de uitvoeringsgeschiedenis:

De tijdstatistieken zijn consistent met de logische waarden en het werkelijke uitvoeringsplan. De subquery liep het snelst (88 ms). Dat wordt gevolgd door de CTE (199 ms). De laatste is het gebruik van tijdelijke tabellen (536ms).

Wat hebben we van Karl geleerd?

In dit specifieke voorbeeld zagen we dat het gebruik van een subquery veel beter is als we de snelste optie willen. Het kan echter een ander verhaal zijn als de set van eisen niet zo is.

Controleer altijd de STATISTICS IO en de Actual Execution Plans om te weten welke techniek je moet gebruiken.

Afhaalmaaltijden

Ik hoop dat je je hoofd niet tegen de muur hebt gestoten om te begrijpen wat een CTE (Common Table Expressions) is. Anders kunt u een CTE (Chronische Traumatische Encephalopathie) krijgen. Grapje terzijde, wat hebben we onthuld?

  • Algemene tabeluitdrukkingen worden tijdelijk resultaatsets genoemd. Het is dichter bij een subquery in gedrag en bereik, maar duidelijker en meer modulair. Het heeft ook een naam.
  • SQL CTE is bedoeld om de code te vereenvoudigen, niet om uw zoekopdracht sneller te maken.
  • Zeven dingen die we hebben geleerd, werken op SQL CTE.
  • Vijf dingen zullen een fout veroorzaken.
  • We hebben ook nogmaals bevestigd dat de STATISTICS IO en het werkelijke uitvoeringsplan u altijd een beter oordeel zullen geven. Het is waar als je een CTE vergelijkt met een subquery en een batch met behulp van tijdelijke tabellen.

Genoten? Dan wachten de social media buttons erop om ingedrukt te worden. Kies je favoriet en deel de liefde!

Lees ook

Hoe CTE kan helpen bij het schrijven van complexe, krachtige zoekopdrachten:een prestatieperspectief


  1. Hoe iif() werkt in SQLite

  2. Installeer mysql-python (Windows)

  3. COSH() Functie in Oracle

  4. Optimaliseer PostgreSQL voor snel testen