sql >> Databáze >  >> RDS >> Database

Vše, co potřebujete vědět o SQL CTE na jednom místě

Karl poprvé slyšel o SQL Server CTE, když hledal něco, co by jeho SQL kód zjednodušilo pro oko. Když se na to podíváte, bolí z toho hlava. Anton, jeho dotčený kolega, se ho zeptal na CTE. Karl si myslel, že Anton naráží na bolest hlavy. Možná to všechno špatně slyšel, a tak odpověděl:"Samozřejmě že ne." Legrační na tom je, že měl na mysli chronickou traumatickou encefalopatii, také CTE – neurodegenerativní onemocnění způsobené opakovanými poraněními hlavy. Ale na základě Karlovy odpovědi Anton s jistotou věděl, že jeho kolega nemá ponětí o tom, co říká.

Jak bláznivý způsob zavedení CTE! Než se tedy dostanete na stejnou loď, ujasněme si, co je SQL CTE nebo Common Table Expressions ve světě SQL?

Základní informace si můžete přečíst zde. Mezitím se dozvíme trochu více o tom, co se stalo v tomto neobvyklém příběhu.

4 Základní informace o CTE v SQL Server

„SQL CTE má název“

Anton začal s myšlenkou, že SQL CTE jsou dočasně pojmenovány sady výsledků. Vzhledem k tomu, že se jedná o dočasný prostředek, má CTE omezený rozsah.

"Takže je to jako dílčí dotaz?" zeptal se Karel.

„Svým způsobem ano. Ale nemůžete pojmenovat dílčí dotaz,“ řekl Anton. „CTE má jméno podobně jako tabulka se jménem. Místo CREATE jej však vytvoříte pomocí WITH.“ Potom napsal syntaxi na papír:

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

„CTE je pryč, když je SELECT hotovo“

Anton pokračoval vysvětlením rozsahu SQL CTE.

„Dočasná tabulka může existovat v rámci postupu nebo globálně. Ale CTE je pryč, když je SELECT hotový,“ řekl to s rýmem. "Totéž, pokud jej použijete pro INSERT, UPDATE nebo DELETE," pokračoval.

„Nemůžete to znovu použít“

„Na rozdíl od zobrazení nebo dočasné tabulky nemůžete znovu použít SQL CTE. Název je tam, takže se na něj můžete odkázat v rámci vnitřního a vnějšího dotazu. Ale to je vše,“ řekl Anton.

"Takže, co je důležitého na SQL CTE?" zeptal se Karel.

„Můžete učinit svůj kód čitelnějším“

"Velká věc?" Anton odpověděl na otázku. „Jde o to, že můžete svůj kód učinit snadno čitelným. Není to to, co hledáte?“

"To je pravda," připustil Karel.

Jaký je tedy další logický krok Karla?

Další informace o CTE v SQL

Další den Karl pokračoval ve svém hledání SQL CTE. Kromě výše uvedeného našel toto:

  • SQL CTE může být nerekurzivní nebo rekurzivní.
  • Tuto myšlenku podporuje nejen SQL Server, ale také MySQL a Oracle. Ve skutečnosti je součástí specifikací SQL-99.
  • I když se používá ke zjednodušení kódu SQL, nezlepšuje výkon.
  • A také nenahradí dílčí dotazy a dočasné tabulky. Každá má své místo a využití.

Zkrátka je to další způsob, jak vyjádřit dotaz .

Ale Karl byl hladový po dalších podrobnostech, a tak pokračoval v hledání toho, co by fungovalo, co ne a jak by to fungovalo v porovnání s dílčími dotazy a dočasnými tabulkami.

Co bude fungovat v SQL Server CTE?

Karl, který hledal další informace o CTE, uvedl níže, co by SQL Server akceptoval. Nahlédněte také do jeho studií.

Přiřazení aliasů vložených nebo externích sloupců

SQL CTE podporují dvě formy přiřazování aliasů sloupců. První je vložený formulář, jako v příkladu níže:

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

Výše uvedený kód používá alias sloupce v definici CTE, když je přiřazen v příkazu SELECT. Všimli jste si COUNT(*) AS NumberOfOrders? ? To je vložený formulář.

Dalším příkladem je externí forma:

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

Sloupce lze také definovat v závorkách po nastavení názvu CTE. Všimněte si WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE v SQL předchází SELECT, INSERT, UPDATE nebo DELETE

Tato další položka je o spotřebě CTE. První a běžný příklad je, když předchází příkazu 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

Co tento příklad ukazuje?

  • Sales_CTE – název CTE.
  • (SalesPersonID, NumberOfOrders) – definice sloupců CTE.
  • SELECT SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader WHERE SalesPersonID NENÍ NULL GROUP BY SalesPersonID – vnitřní SELECT, který definuje CTE.
  • SELECT a.SalesPersonID, CONCAT(P.LastName,’, ‘,P.FirstName,’ ‘,P.MiddleName) AS SalesPerson – vnější dotaz, který spotřebovává CTE. Tento příklad používá SELECT ke spotřebě CTE.
  • OD Sales_CTE a – odkaz vnějšího dotazu na CTE.

Kromě SELECT funguje také s INSERT, UPDATE a DELETE. Zde je příklad použití 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

Ve výše uvedeném seznamu CTE načte poslední mzdu pro zaměstnance 16. Výsledná sada CTE se pak použije k vložení nového záznamu do EmployeePayHistory . Karl svá zjištění elegantně zaznamenal. Také použil vhodné příklady.

Definujte více CTE v 1 dotazu

To je správně. Karl zjistil, že v 1 dotazu je možné více CTE. Zde je příklad:

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

Výše uvedený kód používá 2 CTE v jednom dotazu, konkrétně LatestEmployeePay a PreviousEmployeePay .

Viz vícenásobné CTE

K předchozímu příkladu je toho víc. Všimněte si také, že se můžete VNITŘNÍ PŘIPOJIT k prvnímu CTE k druhému CTE. A konečně, vnější dotaz může spojit oba 2 CTE. LatestEmployeePay byl zmíněn dvakrát.

Předejte argumenty SQL CTE

Argumenty, jako jsou proměnné, lze předávat spolu s 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

Výše uvedený kód začíná deklarováním a nastavením proměnné @SalesPersonID . Hodnota je poté předána CTE k filtrování výsledku.

Použití v KURZORU

Kurzor SQL může používat příkaz SELECT a procházet výsledky. Lze s ním také použít SQL CTE:

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;

Použití dočasné tabulky v rekurzivním CTE

Rekurzivní CTE používá kotevní člen a rekurzivní člen v rámci definice CTE. Pomáhá získat hierarchie v tabulce. SQL CTE může k tomuto účelu použít i dočasnou tabulku. Viz příklad níže:

-- 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 to vysvětlil pitvou tohoto CTE. Zde je návod, jak to jde.

Kotevní člen je první příkaz SELECT s nulovou (0) úrovní posádky:

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

Tento člen kotvy získá kořenový uzel hierarchie. Klauzule WHERE určuje, že kořenová úroveň (HigherRankID IS NULL ).

Rekurzivní člen, který získá podřízené uzly, je extrahován níže:

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

K dispozici je také OPTION (MAXRECURSION 2) použitý ve vnějším dotazu. Rekurzivní CTE se mohou stát problematickými, když je výsledkem rekurzivního dotazu nekonečná smyčka. MAXRECURSION 2 se tomuto nepořádku vyhýbá – omezuje smyčku pouze na 2 rekurze.

Tím končí Karlův seznam toho, co bude fungovat. Ne všechno, na co si myslíme, však může fungovat. V další části se budeme zabývat Karlovými zjištěními v této věci.

Co nebude fungovat v SQL CTE?

Zde máme seznam věcí, které při použití SQL CTE vygenerují chybu.

Žádný středník před SQL CTE

Pokud je před CTE příkaz, musí být tento příkaz ukončen středníkem. Klauzule WITH může fungovat pro jiné účely jako v nápovědě k tabulce, středník tedy odstraní nejednoznačnost. Níže uvedené prohlášení způsobí chybu:

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

První uživatelé, kteří dříve neukončovali příkazy středníkem, narazí na tuto chybu:

Sloupce bez názvu

„Zapomněli jste zadat alias sloupce? Pak jste v další chybě." Karl to řekl ve svém článku a také poskytl ukázkový kód, který sdílím níže:

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

Poté se podívejte na chybovou zprávu:

Duplicitní názvy sloupců

Další chybou související s #2 výše je použití stejného názvu sloupce v CTE. Můžete se toho zbavit běžným příkazem SELECT, ale ne pomocí CTE. Karel měl jiný příklad:

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

ORDER BY klauzule bez TOP nebo OFFSET-FETCH

Standardní SQL neumožňuje ORDER BY v tabulkových výrazech, když jej používáme pro řazení sad výsledků. Pokud se však použije TOP nebo OFFSET-FETCH, ORDER BY se stane pomůckou pro filtrování.

Zde je Karlův příklad s použitím příkazu 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

Všimněte si, že se jedná o stejný příklad, jaký jsme měli dříve, ale tentokrát není uvedeno TOP. Zkontrolujte chybu:

Počet sloupců není stejný jako definice seznamu sloupců

Karl použil stejný příklad rekurzivního CTE, ale vyjmul sloupec v kotevním členu:

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;

Výše uvedený kód používá seznam tří sloupců s externím formulářem, ale kotevní člen měl pouze 2 sloupce. Není dovoleno. Pokud uděláte takovou chybu, dojde k chybě:

Další věci, které nejsou povoleny v CTE

Kromě výše uvedeného seznamu je zde několik dalších Karlových zjištění, která spouštějí chyby, pokud jej omylem použijete v SQL CTE.

  • Použití klauzule SELECT INTO, OPTION s nápovědou k dotazu a použití FOR BROWSE.
  • Odlišná data a typy ve sloupcích kotevních členů ve srovnání se sloupci rekurzivních členů.
  • Mít následující klíčová slova v rekurzivním členu rekurzivního CTE:
    • TOP
    • OUTER JOIN (ale INNER JOIN je povoleno)
    • GROUP BY a HAVING
    • Poddotazy
    • VYBRAT DISTINCT
  • Použití skalární agregace.
  • Použití poddotazů v rekurzivním členu.
  • Mít vnořené SQL CTE.

SQL CTE vs. dočasné tabulky vs. poddotazy

Občas můžete přepsat SQL CTE pomocí poddotazu. Občas také můžete z důvodu výkonu rozdělit SQL CTE pomocí dočasných tabulek. Jako každý jiný dotaz musíte zkontrolovat Plán skutečného provedení a STATISTICS IO, abyste věděli, kterou možnost zvolit. SQL CTE může být šetrné k očím, ale pokud narazíte na překážku výkonu, použijte jinou možnost . Žádná možnost není rychlejší než druhá.

Podívejme se na tři dotazy z Karlových prací, které přinášejí stejné výsledky. Jeden používá SQL CTE, druhý používá poddotaz a třetí používá dočasnou tabulku. Pro jednoduchost Karl použil malou sadu výsledků.

Kód a sada výsledků

Začalo to použitím více SQL CTE.

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

Další je poddotaz. Jak jste si všimli, CTE vypadá modulárně a čitelně, ale poddotaz níže je kratší:

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 se také pokusil rozdělit jej na malé kousky kódu a poté zkombinovat výsledky pomocí dočasných tabulek.

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

Podívejte se na tyto tři způsoby, jak výsledky získat aktuální a předchozí platy pro zaměstnance 16. Jsou stejné:

Logické čtení

Co spotřebovává nejvíce prostředků SQL Server? Podívejme se na STATISTICS IO. Karl použil statisticparser.com k pěknému formátování výsledků – pro nás dobré.

Obrázek 7 ukazuje logické čtení použití CTE vs. použití poddotazu:

Dále se podívejte na celkový počet logických čtení při rozdělování kódu na malé kousky pomocí dočasných tabulek:

Co tedy spotřebuje více zdrojů? Karl je seřadil kvůli přehlednosti.

  1. Poddotaz – 4 logická čtení (VÍTĚZ!).
  2. SQL CTE – 6 logických čtení.
  3. Dočasné tabulky – 8 logických čtení.

V tomto příkladu bude nejrychlejší dílčí dotaz.

Skutečný plán provádění

Aby dal smysl logickým údajům, které jsme získali ze STATISTICS IO, zkontroloval Karl také skutečný plán provádění. Začal z CTE:

Z tohoto plánu můžeme vypozorovat několik věcí:

  • LatestEmployeePay CTE byla vyhodnocena dvakrát při použití ve vnějším dotazu a při připojení k PreviousEmployeePay . Takže pro to vidíme 2 TOP uzly.
  • Vidíme PreviousEmployeePay vyhodnoceno jednou.

Poté se podívejte na Plán skutečného provedení dotazu s poddotazem:

Zde je několik zjevných věcí:

  • Plán je jednodušší.
  • Je to jednodušší, protože dílčí dotaz na získání poslední mzdy je vyhodnocen pouze jednou.
  • Není divu, že logické čtení je menší ve srovnání s logickým čtením dotazu s CTE.

Nakonec zde je skutečný plán provádění, když Karel používal dočasné tabulky:

Protože se jedná o dávku tří příkazů, vidíme v plánu také tři diagramy. Všechny tři jsou jednoduché, ale společný plán není tak jednoduchý jako plán dotazu s poddotazem.

Časová statistika

Pomocí řešení dbForge Studio for SQL Server můžete porovnávat časové statistiky v Query Profiler. Chcete-li to provést, podržte klávesu CTRL a klikněte na názvy výsledků každého dotazu v historii provádění:

Časové statistiky jsou konzistentní s logickými čteními a skutečným plánem provádění. Poddotaz běžel nejrychleji (88 ms). Následuje CTE (199 ms). Poslední je použití dočasných tabulek (536 ms).

Co jsme se tedy od Karla naučili?

V tomto konkrétním příkladu jsme viděli, že použití poddotazu je mnohem lepší, když chceme nejrychlejší možnost. Pokud však soubor požadavků takový není, může to být jiný příběh.

Vždy zkontrolujte STATISTICS IO a skutečné plány provádění, abyste věděli, kterou techniku ​​použít.

Takové věci

Doufám, že jste nenarazili hlavou do zdi, abyste pochopili, co je to CTE (Common Table Expressions). V opačném případě můžete dostat CTE (chronická traumatická encefalopatie). Srandu stranou, co jsme odhalili?

  • Běžné tabulkové výrazy jsou dočasně pojmenované sady výsledků. Chováním a rozsahem se blíží poddotazu, ale je přehlednější a modulárnější. Má také jméno.
  • SQL CTE slouží ke zjednodušení kódu, nikoli ke zrychlení dotazu.
  • Sedm věcí, které jsme se naučili, bude fungovat na SQL CTE.
  • Pět věcí způsobí chybu.
  • Znovu jsme také potvrdili, že STATISTICS IO a Actual Execution Plan vám vždy poskytnou lepší úsudek. Platí to, pokud porovnáte CTE s poddotazem a dávkou pomocí dočasných tabulek.

Užil si to? Poté tlačítka sociálních sítí čekají na zmáčknutí. Vyberte si svou oblíbenou a sdílejte lásku!

Přečtěte si také

Jak může CTE pomoci při psaní složitých a účinných dotazů:Perspektiva výkonu


  1. Příkaz Oracle CREATE TABLE v PL/SQL s 10 příklady

  2. PSQLException:ERROR:vztah TABLE_NAME neexistuje

  3. Jak mohu použít UUID v SQLAlchemy?

  4. SQLSTATE[HY000] [1045] Přístup odepřen uživateli 'username'@'localhost' pomocí CakePHP