sql >> Databasteknik >  >> RDS >> Database

Allt du behöver veta om SQL CTE på ett ställe

Första gången Karl hörde talas om SQL Server CTE var när han letade efter något för att göra sin SQL-kod lättare för ögat. Det är typ av huvudvärk när man tittar på det. Anton, hans oroliga kollega, frågade honom om CTE. Karl trodde att Anton syftade på sin huvudvärk. Han kanske hörde allt fel, så han svarade:"Självklart inte." Det roliga är att han syftade på kronisk traumatisk encefalopati, också en CTE – en neurodegenerativ sjukdom som orsakas av upprepade huvudskador. Men baserat på Karls svar visste Anton med säkerhet att hans kollega hade ingen aning om vad han sa.

Vilket galet sätt att introducera CTEs! Så, innan du sätter dig i samma båt, låt oss förtydliga, vad är SQL CTE eller vanliga tabelluttryck i SQL-världen?

Du kan läsa grunderna här. Under tiden kommer vi att lära oss lite mer om vad som hände i den här ovanliga historien.

4 grundläggande saker om CTE i SQL Server

"En SQL CTE har ett namn"

Anton började med idén att SQL CTE tillfälligt kallas för resultatuppsättningar. Eftersom CTE är ett temporärt sätt är omfattningen begränsad.

"Så, det är som en underfråga?" frågade Karl.

"På sätt och vis, ja. Men du kan inte namnge en underfråga, sa Anton. "En CTE har ett namn ungefär som ett bord med ett namn. Men istället för CREATE använder du WITH för att skapa den.” Sedan skrev han syntaxen på papper:

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

"CTE är borta när SELECT är klart"

Anton fortsatte med att förklara omfattningen av SQL CTE.

"En tillfällig tabell kan existera inom procedurens räckvidd eller globalt. Men CTE är borta när SELECT är klart,” sa han det med ett rim. "Samma sak om du använder den för INSERT, UPDATE eller DELETE," fortsatte han.

"Du kan inte återanvända det"

"Till skillnad från en vy eller en temporär tabell kan du inte återanvända SQL CTE. Namnet finns där, så du kan referera till det inom den inre och den yttre frågan. Men det är allt”, berättade Anton.

"Så, vad är grejen med SQL CTE:er?" frågade Karl.

"Du kan göra din kod mer läsbar"

"The big deal?" Anton svarade på frågan. "Det är att du kan göra din kod lättläsbar. Är det inte det du letar efter?”

"Det stämmer", erkände Karl.

Så, vad är nästa logiska steg för Karl att göra?

Extra saker om CTE i SQL

Dagen efter fortsatte Karl sitt sökande efter SQL CTE. Bortsett från ovanstående, här är vad han hittade:

  • SQL CTE kan vara icke-rekursiv eller rekursiv.
  • Inte bara SQL Server utan även MySQL och Oracle stödjer idén. Det är faktiskt en del av SQL-99-specifikationerna.
  • Även om det används för att förenkla SQL-kod, förbättrar det inte prestandan.
  • Och det kommer inte heller att ersätta underfrågor och tillfälliga tabeller. Var och en har sin plats och användning.

Kort sagt, det är ett annat sätt att uttrycka en fråga .

Men Karl var sugen på mer detaljer, så han fortsatte att leta efter vad som skulle fungera, vad som inte skulle fungera och hur det skulle fungera kontra underfrågor och tillfälliga tabeller.

Vad fungerar i SQL Server CTE?

Karl grävde vidare för att reda ut mer om CTE och listade nedan vad SQL Server skulle acceptera. Ta en titt på hans studier också.

Tilldela inline eller externa kolumnalias

SQL CTE:er stöder två former av tilldelning av kolumnalias. Den första är inline-formuläret, som exemplet nedan:

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

Ovanstående kod använder kolumnalias inom CTE-definitionen när den tilldelas i SELECT-satsen. Lade du märke till COUNT(*) AS NumberOfOrders ? Det är det inbäddade formuläret.

Ett annat exempel är den externa formen:

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

Kolumnerna kan också definieras inom parentes efter inställning av CTE-namnet. Lägg märke till WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE i SQL föregår en SELECT, INSERT, UPDATE eller DELETE

Nästa punkt handlar om att konsumera CTE. Det första och vanliga exemplet är när det föregår en SELECT-sats.

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

Vad visar detta exempel?

  • Sales_CTE – namnet på CTE.
  • (Säljare-ID, NumberOfOrders) – definitionen av CTE-kolumner.
  • VÄLJ Säljare-ID, ANTAL(*) FRÅN Sales.SalesOrderHeader DÄR Säljare-ID INTE ÄR NULL GRUPPER EFTER Säljar-ID – den inre SELECT som definierar CTE.
  • SELECT a.SalePersonID, CONCAT(P.LastName,', ',P.FirstName,' ',P.MiddleName) AS SalesPerson – den yttre frågan som förbrukar CTE. Det här exemplet använder en SELECT för att konsumera CTE.
  • FRÅN Sales_CTE a – referensen för den yttre frågan till CTE.

Förutom en SELECT fungerar den också med INSERT, UPDATE och DELETE. Här är ett exempel på hur du använder 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

I listan ovan hämtar CTE den senaste lönen för anställd 16. Resultatuppsättningen för CTE används sedan för att infoga ett nytt rekord i EmployeePayHistory . Karl registrerade sina fynd elegant. Han använde också passande exempel.

Definiera flera CTE:er i en fråga

Det är rätt. Karl fann att flera CTE:er är möjliga i en fråga. Här är ett exempel:

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

Ovanstående kod använder 2 CTE i en fråga, nämligen LatestEmployeePay och PreviousEmployeePay .

Se en CTE flera gånger

Det finns mer i det föregående exemplet. Lägg också märke till att du kan INNER JOIN den första CTE till den andra CTE. Slutligen kan den yttre frågan gå med i båda de två CTE:erna. LatestEmployeePay har hänvisats till två gånger.

Skicka argument till en SQL CTE

Argument, såsom variabler, kan skickas längs en 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

Ovanstående kod börjar med att deklarera och ställa in en variabel @SalesPersonID . Värdet skickas sedan till CTE för att filtrera resultatet.

Använd i en CURSOR

En SQL-markör kan använda en SELECT-sats och gå igenom resultaten. Dessutom kan en SQL CTE användas med den:

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;

Använd en tillfällig tabell i en rekursiv CTE

Rekursiv CTE använder en ankardel och en rekursiv medlem inom CTE-definitionen. Det hjälper till att få hierarkier i en tabell. SQL CTE kan också använda en temporär tabell för detta ändamål. Se ett exempel nedan:

-- 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 förklarade genom att dissekera denna CTE. Så här går det till.

Ankarmedlemmen är den första SELECT-satsen med noll (0) besättningsnivå:

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

Denna ankarmedlem får hierarkins rotnod. WHERE-satsen anger att rotnivån (HigherRankID IS NULL ).

Den rekursiva medlemmen som kommer att få barnnoderna extraheras nedan:

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

Det finns också ett ALTERNATIV (MAXRECURSION 2) används i den yttre frågan. Rekursiva CTE:er kan bli problematiska när en oändlig loop resulterar från den rekursiva frågan. MAXRECURSION 2 undviker denna röra – den begränsar loopen till endast 2 rekursioner.

Detta avslutar Karls lista över vad som kommer att fungera. Men allt vi tänker på kanske inte fungerar. Nästa avsnitt kommer att diskutera Karls resultat om dessa.

Vad fungerar inte i SQL CTE?

Här har vi en lista över saker som kommer att generera ett fel när du använder SQL CTE.

Inget semikolon före SQL CTE

Om det finns en sats före CTE, måste den satsen avslutas med ett semikolon. WITH-satsen kan fungera för andra syften som i en tabelltips, och semikolonet kommer därför att ta bort tvetydighet. Uttalandet nedan kommer att orsaka ett fel:

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

Nybörjare som brukade inte avsluta uttalanden med semikolon stöter på detta fel:

Onamngivna kolumner

"Har du glömt att sätta ett kolumnalias? Då har du ett annat fel." Karl sa detta i sin tidning och gav också en exempelkod som jag delar nedan:

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

Titta sedan på felmeddelandet:

Duplicera kolumnnamn

Ett annat fel relaterat till #2 ovan är att använda samma kolumnnamn inom CTE. Du kan komma undan med det i en vanlig SELECT-sats, men inte med en CTE. Karl hade ett annat exempel:

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

ORDER BY Klausul Utan TOP eller OFFSET-FETCH

Standard SQL tillåter inte ORDER BY i tabelluttryck när vi använder den för att sortera resultatuppsättningarna. Men om TOP eller OFFSET-FETCH används, blir ORDER BY ett filtreringshjälpmedel.

Här är Karls exempel med en BESTÄLLNING AV:

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

Lägg märke till att det är samma exempel som vi hade tidigare, men den här gången är TOP inte specificerat. Kolla in felet:

Antalet kolumner är inte detsamma som kolumnlistans definition

Karl använde samma rekursiva CTE-exempel, men han tog ut en kolumn i ankarelementet:

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;

Koden ovan använder en lista med tre kolumner med en extern form, men ankarmedlemmen hade bara 2 kolumner. Det är inte tillåtet. Om du gör ett misstag som detta utlöser ett fel:

Andra saker som inte är tillåtna i en CTE

Bortsett från listan ovan, här är några fler av Karls upptäckter som utlöser fel om du använder den av misstag i en SQL CTE.

  • Använda SELECT INTO, OPTION-satsen med frågetips och använda FOR Browse.
  • Olika data och typer i kolumnerna för ankarmedlemmar jämfört med de rekursiva medlemskolumnerna.
  • Att ha följande nyckelord i en rekursiv medlem av en rekursiv CTE:
    • TOPP
    • YTTRE JOIN (Men INNER JOIN är tillåten)
    • GRUPPERA EFTER och HA
    • Undersökningar
    • VÄLJ DISTINKT
  • Använda skalär aggregering.
  • Använda underfrågor i en rekursiv medlem.
  • Har SQL CTE:er kapslade.

SQL CTE vs. tillfälliga tabeller vs. underfrågor

Ibland kan du skriva om en SQL CTE med hjälp av en underfråga. Ibland kan du också bryta ner en SQL CTE med hjälp av tillfälliga tabeller av prestandaskäl. Som alla andra frågor måste du kontrollera den faktiska utförandeplanen och STATISTICS IO för att veta vilket alternativ du ska välja. SQL CTE kan vara vänlig för ögonen, men om du träffar en prestandavägg, använd ett annat alternativ . Inget alternativ är snabbare än det andra.

Låt oss undersöka tre frågor från Karls artiklar som ger samma resultat. Den ena använder en SQL CTE, den andra använder en underfråga och den tredje använder en temporär tabell. För enkelhetens skull använde Karl en liten resultatuppsättning.

Koden och resultatuppsättningen

Det började med att använda flera SQL CTE:er.

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

Nästa är en underfråga. Som du märker ser CTE modulär och läsbar ut, men underfrågan nedan är kortare:

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 försökte också dela upp den i små bitar av kod och sedan kombinera resultaten med hjälp av tillfälliga tabeller.

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

Ta en titt på dessa tre sätt att få nuvarande och tidigare löner för anställd 16. De är desamma:

The Logical Reads

Vad förbrukar mest SQL Server-resurser? Låt oss titta på STATISTICS IO. Karl använde statistikparser.com för att formatera resultaten snyggt – bra för oss.

Figur 7 visar logiska läsningar av att använda en CTE jämfört med att använda en underfråga:

Se sedan den totala logiska läsningen när du delar koden i små bitar med hjälp av tillfälliga tabeller:

Så vad förbrukar mer resurser? Karl rankade dem för tydlighetens skull.

  1. Subquery – 4 logiska läsningar (VINNARE!).
  2. SQL CTE – 6 logiska läsningar.
  3. Templartabeller – 8 logiska läsningar.

I det här exemplet kommer den snabbaste att vara underfrågan.

Den faktiska genomförandeplanen

För att förstå de logiska läsningarna vi fick från STATISTICS IO, kontrollerade Karl också den faktiska utförandeplanen. Han började från CTE:

Vi kan observera några saker från den här planen:

  • Senaste EmployeePay CTE utvärderades två gånger när den användes i den yttre frågan och när den kopplades till PreviousEmployeePay . Så vi ser 2 TOPP-noder för detta.
  • Vi ser PreviousEmployeePay utvärderas en gång.

Titta sedan på den faktiska exekveringsplanen för frågan med en underfråga:

Det finns några uppenbara saker här:

  • Planet är enklare.
  • Det är enklare eftersom underfrågan för att få den senaste lönen endast utvärderas en gång.
  • Inte konstigt att de logiska läsningarna är mindre jämfört med de logiska läsningarna av frågan med en CTE.

Slutligen, här är den faktiska utförandeplanen när Karl använde tillfälliga tabeller:

Eftersom det är en grupp med tre påståenden ser vi också tre diagram i planen. Alla tre är enkla, men den kollektiva planen är inte lika enkel som planen för frågan med underfrågan.

Tidsstatistiken

Med hjälp av dbForge Studio för SQL Server-lösningen kan du jämföra tidsstatistiken i Query Profiler. För det, håll ned CTRL-tangenten och klicka på resultatnamnen för varje fråga i exekveringshistoriken:

Tidsstatistiken överensstämmer med de logiska avläsningarna och den faktiska utförandeplanen. Underfrågan gick snabbast (88 ms). Det följs av CTE (199ms). Den sista är användningen av tillfälliga tabeller (536ms).

Så, vad har vi lärt oss av Karl?

I det här exemplet såg vi att det är mycket bättre att använda en underfråga när vi vill ha det snabbaste alternativet. Det kan dock bli en annan historia om kravuppsättningen inte är sådan.

Kontrollera alltid STATISTICS IO och de faktiska exekveringsplanerna för att veta vilken teknik du ska använda.

Hämtmat

Jag hoppas att du inte stötte huvudet mot väggen för att förstå vad en CTE (Common Table Expressions) är. Annars kan du få en CTE (kronisk traumatisk encefalopati). Skämt åsido, vad avslöjade vi?

  • Vanliga tabelluttryck är tillfälligt namngivna resultatuppsättningar. Det är närmare en underfråga i beteende och omfattning men tydligare och mer modulärt. Den har också ett namn.
  • SQL CTE är till för att förenkla kod, inte för att göra din fråga snabbare.
  • Sju saker vi har lärt oss kommer att fungera på SQL CTE.
  • Fem saker kommer att utlösa ett fel.
  • Vi har också bekräftat ännu en gång att STATISTICS IO och faktiska utförandeplan alltid kommer att ge dig en bättre bedömning. Det är sant om du jämför en CTE med en underfråga och en batch med hjälp av tillfälliga tabeller.

Njöt av det? Då väntar knapparna på sociala medier på att bli nedtryckta. Välj din favorit och dela kärleken!

Läs också

Hur CTE kan hjälpa till att skriva komplexa, kraftfulla frågor:ett prestationsperspektiv


  1. SQLPlus - spoolning till flera filer från PL/SQL-block

  2. Oracle felhantering

  3. Grundläggande SQL-frågor

  4. gem install pg --with-pg-config fungerar, paketet misslyckas