Database
 sql >> Baza danych >  >> RDS >> Database

Wszystko, co musisz wiedzieć o SQL CTE w jednym miejscu

Karl po raz pierwszy usłyszał o SQL Server CTE, gdy szukał czegoś, co sprawi, że jego kod SQL będzie łatwiejszy dla oka. To rodzaj bólu głowy, kiedy na to patrzysz. Anton, jego zaniepokojony kolega, zapytał go o CTE. Karl pomyślał, że Anton miał na myśli ból głowy. Może wszystko źle usłyszał, więc odpowiedział:„Oczywiście, że nie”. Zabawne jest to, że miał na myśli przewlekłą encefalopatię pourazową, także CTE – chorobę neurodegeneracyjną spowodowaną powtarzającymi się urazami głowy. Ale na podstawie odpowiedzi Karla Anton wiedział na pewno, że jego kolega nie ma pojęcia o tym, co mówi.

Cóż za szalony sposób na wprowadzenie CTE! Tak więc, zanim przejdziesz do tej samej łodzi, wyjaśnijmy, czym jest SQL CTE lub Common Table Expressions w świecie SQL?

Podstawy możesz przeczytać tutaj. Tymczasem dowiemy się nieco więcej o tym, co wydarzyło się w tej niezwykłej historii.

4 podstawowe informacje o CTE w SQL Server

„CTE SQL ma nazwę”

Anton zaczął od pomysłu, że CTE SQL są tymczasowo nazwane zestawami wyników. Będąc środkiem tymczasowym, CTE ma ograniczony zakres.

„Więc to jest jak podzapytanie?” – zapytał Karl.

„W pewnym sensie tak. Ale nie możesz nazwać podzapytania – powiedział Anton. „CTE ma nazwę podobnie jak stół z nazwą. Jednak zamiast CREATE, do jej tworzenia używasz WITH.” Następnie napisał składnię na papierze:

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

„CTE zniknęło po zakończeniu WYBORU”

Anton kontynuował, wyjaśniając zakres SQL CTE.

„Tabela tymczasowa może istnieć w zakresie procedury lub globalnie. Ale CTE zniknęło po zakończeniu SELECT” – powiedział rymowankiem. „To samo, jeśli używasz go do INSERT, UPDATE lub DELETE” – kontynuował.

„Nie możesz tego ponownie użyć”

„W przeciwieństwie do widoku lub tabeli tymczasowej, nie można ponownie użyć SQL CTE. Nazwa jest tam, więc możesz odnieść się do niej w wewnętrznym i zewnętrznym zapytaniu. Ale to wszystko” – powiedział Anton.

„Więc, o co chodzi w SQL CTE?” – zapytał Karl.

„Możesz uczynić swój kod bardziej czytelnym”

"Wielka sprawa?" Anton odpowiedział na pytanie. „Chodzi o to, że możesz sprawić, by Twój kod był łatwy do odczytania. Czy nie tego właśnie szukasz?”

– Zgadza się – przyznał Karl.

Jaki jest więc następny logiczny krok do zrobienia dla Karla?

Dodatkowe informacje o CTE w SQL

Następnego dnia Karl kontynuował poszukiwania SQL CTE. Oprócz powyższego, oto, co znalazł:

  • SQL CTE może być nierekurencyjne lub rekurencyjne.
  • Nie tylko SQL Server, ale także MySQL i Oracle wspierają tę ideę. W rzeczywistości jest to część specyfikacji SQL-99.
  • Chociaż służy do uproszczenia kodu SQL, nie poprawia wydajności.
  • Nie zastąpi też podzapytań i tabel tymczasowych. Każdy ma swoje miejsce i zastosowanie.

W skrócie, to kolejny sposób wyrażenia zapytania .

Ale Karl był głodny więcej szczegółów, więc nadal szukał, co zadziała, a co nie i jak będzie działać w porównaniu z podzapytaniami i tabelami tymczasowymi.

Co będzie działać w SQL Server CTE?

Kopiąc dalej, aby dowiedzieć się więcej o CTE, Karl wymienił poniżej, co SQL Server zaakceptowałby. Rzuć okiem na jego studia.

Przypisz aliasy kolumn wbudowanych lub zewnętrznych

SQL CTE obsługuje dwie formy przypisywania aliasów kolumn. Pierwszym z nich jest formularz wbudowany, jak w poniższym przykładzie:

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

Powyższy kod używa aliasu kolumny w definicji CTE, gdy zostanie przypisany w instrukcji SELECT. Czy zauważyłeś COUNT(*) AS NumberOfOrders? ? To jest formularz wbudowany.

Teraz innym przykładem jest formularz zewnętrzny:

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

Kolumny można również zdefiniować w nawiasach po ustawieniu nazwy CTE. Zwróć uwagę na WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE w SQL poprzedza SELECT, INSERT, UPDATE lub DELETE

Kolejny punkt dotyczy zużycia CTE. Pierwszym i powszechnym przykładem jest sytuacja, w której poprzedza ona instrukcję 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 pokazuje ten przykład?

  • Sprzedaż_CTE – nazwa CTE.
  • (SalesPersonID, NumberOfOrders) – definicja kolumn CTE.
  • SELECT SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader WHERE SalesPersonID IS NOT NULL GROUP BY SalesPersonID – wewnętrzny SELECT, który definiuje CTE.
  • SELECT a.IdentyfikatorSprzedaży, CONCAT(P.Nazwisko,’, ‚,P.Imię,’ ‚,P.DrugieNazwisko) AS Sprzedawca – zewnętrzne zapytanie, które zużywa CTE. Ten przykład używa SELECT do wykorzystania CTE.
  • OD Sales_CTE a – odniesienie zewnętrznego zapytania do CTE.

Oprócz SELECT działa również z INSERT, UPDATE i DELETE. Oto przykład użycia 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

W powyższym zestawieniu CTE pobiera ostatnie wynagrodzenie dla pracownika 16. Zestaw wyników CTE jest następnie używany do wstawienia nowego rekordu w EmployeePayHistory . Karl elegancko zapisał swoje odkrycia. Użył również pasujących przykładów.

Zdefiniuj wiele CTE w jednym zapytaniu

Zgadza się. Karl odkrył, że w jednym zapytaniu możliwe jest wiele CTE. Oto przykład:

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

Powyższy kod wykorzystuje 2 CTE w jednym zapytaniu, a mianowicie LatestEmployeePay i PoprzedniEmployeePay .

Odwołaj się do wielokrotnego CTE

W poprzednim przykładzie jest więcej. Zauważ również, że możesz INNER JOIN pierwsze CTE do drugiego CTE. Wreszcie zapytanie zewnętrzne może połączyć oba CTE. Najnowsza płatność pracownika została wymieniona dwukrotnie.

Przekaż argumenty do CTE SQL

Argumenty, takie jak zmienne, mogą być przekazywane wzdłuż 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

Powyższy kod zaczyna się od zadeklarowania i ustawienia zmiennej @SalesPersonID . Wartość jest następnie przekazywana do CTE w celu przefiltrowania wyniku.

Użyj w KURSORZE

Kursor SQL może używać instrukcji SELECT i przeglądać wyniki w pętli. Można z nim również użyć 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;

Użyj tabeli tymczasowej w rekurencyjnym CTE

Rekurencyjne CTE wykorzystuje zakotwiczony element członkowski i rekurencyjny element członkowski w definicji CTE. Pomaga uzyskać hierarchie w tabeli. SQL CTE może również wykorzystać do tego celu tabelę tymczasową. Zobacz przykład poniżej:

-- 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 wyjaśnił to, przeprowadzając sekcję CTE. Oto jak to działa.

Zakotwiczony członek to pierwsza instrukcja SELECT z poziomem załogi zero (0):

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

Ten zakotwiczony element członkowski pobiera węzeł główny hierarchii. Klauzula WHERE określa, że ​​poziom główny (HigherRankID JEST NULL ).

Składnik rekurencyjny, który otrzyma węzły podrzędne, jest wyodrębniony poniżej:

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

Dostępna jest również OPCJA (MAXRECURSION 2) używane w zapytaniu zewnętrznym. Rekurencyjne CTE mogą stać się problematyczne, gdy z zapytania rekurencyjnego wynika nieskończona pętla. MAXRECURSION 2 pozwala uniknąć tego bałaganu – ogranicza pętlę tylko do 2 rekursji.

To kończy listę Karla tego, co zadziała. Jednak nie wszystko, o czym myślimy, może działać. W następnej sekcji omówione zostaną odkrycia Karla na ten temat.

Co nie będzie działać w SQL CTE?

Tutaj mamy listę rzeczy, które wygenerują błąd podczas korzystania z SQL CTE.

Brak średnika poprzedzającego CTE SQL

Jeśli istnieje oświadczenie przed CTE, należy je zakończyć średnikiem. Klauzula WITH może działać do innych celów, na przykład w podpowiedzi do tabeli, dlatego średnik usunie niejednoznaczność. Poniższe oświadczenie spowoduje błąd:

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

Osoby po raz pierwszy, które nie przerywały instrukcji ze średnikiem, napotykają ten błąd:

Kolumny bez nazwy

„Zapomniałeś wstawić alias kolumny? Wtedy czeka cię kolejny błąd. Karl powiedział to w swoim artykule, a także dostarczył przykładowy kod, który udostępniam poniżej:

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

Następnie spójrz na komunikat o błędzie:

Zduplikowane nazwy kolumn

Innym błędem związanym z punktem 2 powyżej jest użycie tej samej nazwy kolumny w CTE. Możesz ujść mu na sucho w normalnym poleceniu SELECT, ale nie za pomocą CTE. Karl miał inny przykład:

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

Klauzula ORDER BY bez TOP lub OFFSET-FETCH

Standardowy SQL nie zezwala na ORDER BY w wyrażeniach tabelowych, gdy używamy go do sortowania zestawów wyników. Jednak w przypadku użycia opcji TOP lub OFFSET-FETCH funkcja ORDER BY staje się pomocą przy filtrowaniu.

Oto przykład Karla używającego 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

Zauważ, że jest to ten sam przykład, który mieliśmy wcześniej, ale tym razem TOP nie jest określony. Sprawdź błąd:

Liczba kolumn nie jest taka sama jak w definicji listy kolumn

Karl użył tego samego przykładu rekurencyjnego CTE, ale wyjął kolumnę z elementu zakotwiczenia:

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;

Powyższy kod używa listy z trzema kolumnami z formularzem zewnętrznym, ale element zakotwiczenia miał tylko 2 kolumny. To jest niedozwolone. Popełnienie takiego błędu spowoduje błąd:

Inne rzeczy niedozwolone w CTE

Oprócz powyższej listy, oto kilka innych odkryć Karla, które powodują błędy, jeśli użyjesz ich przez pomyłkę w SQL CTE.

  • Użycie klauzuli SELECT INTO, OPTION z podpowiedziami do zapytania i użycie FOR BROWSE.
  • Różne dane i typy w kolumnach zakotwiczonych elementów członkowskich w porównaniu z rekurencyjnymi kolumnami elementów członkowskich.
  • Posiadanie następujących słów kluczowych w rekurencyjnym elemencie rekurencyjnego CTE:
    • GÓRA
    • ZŁĄCZENIE ZEWNĘTRZNE (ale DOŁĄCZENIE WEWNĘTRZNE jest dozwolone)
    • GRUPA WEDŁUG i POSIADAJĄC
    • Podzapytania
    • WYBIERZ WYRÓŻNIENIE
  • Korzystanie z agregacji skalarnej.
  • Korzystanie z podzapytań w członie rekurencyjnym.
  • Zagnieżdżanie CTE SQL.

SQL CTE a tabele tymczasowe a podzapytania

Czasami można przepisać CTE SQL za pomocą podzapytania. Czasami można również podzielić CTE SQL za pomocą tabel tymczasowych ze względu na wydajność. Jak w przypadku każdego innego zapytania, aby wiedzieć, którą opcję wybrać, musisz sprawdzić Aktualny plan wykonania i STATISTICS IO. SQL CTE może być przyjazny dla oczu, ale jeśli trafisz na barierę wydajności, użyj innej opcji . Żadna opcja nie jest szybsza od drugiej.

Przyjrzyjmy się trzem zapytaniom z artykułów Karla, które przynoszą te same wyniki. Jedna używa CTE SQL, druga używa podzapytania, a trzecia używa tabeli tymczasowej. Dla uproszczenia Karl użył małego zestawu wyników.

Kod i zestaw wyników

Zaczęło się od użycia wielu 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

Kolejne to podzapytanie. Jak zauważyłeś, CTE wygląda na modułowe i czytelne, ale poniższe podzapytanie jest krótsze:

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 próbował również podzielić go na małe fragmenty kodu, a następnie połączyć wyniki za pomocą tabel tymczasowych.

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

Spójrz na te trzy sposoby uzyskiwania bieżących i poprzednich wynagrodzeń dla Pracownika 16. Są takie same:

Odczyty logiczne

Co zużywa najwięcej zasobów SQL Server? Spójrzmy na IO STATYSTYKI. Karl użył Statisticsparser.com, aby ładnie sformatować wyniki – dobrze dla nas.

Rysunek 7 pokazuje logiczne odczyty użycia CTE w porównaniu z użyciem podzapytania:

Następnie zobacz całkowitą liczbę logicznych odczytów podczas dzielenia kodu na małe kawałki przy użyciu tabel tymczasowych:

Co więc zużywa więcej zasobów? Karl ocenił je pod kątem przejrzystości.

  1. Podzapytanie – 4 logiczne odczyty (ZWYCIĘZCA!).
  2. SQL CTE – 6 logicznych odczytów.
  3. Tabele temp – 8 odczytów logicznych.

W tym przykładzie najszybsze będzie podzapytanie.

Rzeczywisty plan wykonania

Aby nadać sens logicznym odczytom, które otrzymaliśmy z IO STATYSTYK, Karl sprawdził również rzeczywisty plan wykonania. Zaczął od CTE:

Z tego planu możemy zaobserwować kilka rzeczy:

  • LatestEmployeePay CTE zostało ocenione dwukrotnie, gdy zostało użyte w zapytaniu zewnętrznym i gdy jest połączone z PreviousEmployeePay . Tak więc widzimy w tym celu 2 TOP węzły.
  • Widzimy PoprzedniEmployeePay oceniane raz.

Następnie spójrz na rzeczywisty plan wykonania zapytania z podzapytaniem:

Jest tu kilka oczywistych rzeczy:

  • Plan jest prostszy.
  • Jest to prostsze, ponieważ podzapytanie w celu uzyskania ostatniego wynagrodzenia jest oceniane tylko raz.
  • Nic dziwnego, że odczyty logiczne są mniej w porównaniu z odczytami logicznymi zapytania z CTE.

Na koniec, oto rzeczywisty plan wykonania, w którym Karl używał tabel tymczasowych:

Ponieważ jest to zestaw trzech oświadczeń, w planie widzimy również trzy diagramy. Wszystkie trzy są proste, ale plan zbiorczy nie jest tak prosty jak plan zapytania z podzapytaniem.

Statystyka czasu

Korzystając z rozwiązania dbForge Studio dla SQL Server, możesz porównać statystyki czasu w Query Profiler. W tym celu przytrzymaj klawisz CTRL i kliknij nazwy wyników każdego zapytania w historii wykonania:

Statystyki czasowe są zgodne z odczytami logicznymi i Rzeczywistym Planem Wykonawczym. Podzapytanie przebiegło najszybciej (88 ms). Następnie następuje CTE (199 ms). Ostatnim jest użycie tabel tymczasowych (536 ms).

Czego więc nauczyliśmy się od Karla?

W tym konkretnym przykładzie widzieliśmy, że użycie podzapytania jest znacznie lepsze, gdy zależy nam na najszybszej opcji. Jednak może to być inna historia, jeśli zestaw wymagań nie jest taki.

Zawsze sprawdzaj IO STATYSTYKI i Rzeczywiste plany wykonania, aby wiedzieć, której techniki użyć.

Na wynos

Mam nadzieję, że nie uderzyłeś głową o ścianę, aby zrozumieć, czym jest CTE (Wspólne Wyrażenia Tabeli). W przeciwnym razie możesz dostać CTE (przewlekła encefalopatia pourazowa). Żarty na bok, co ujawniliśmy?

  • Typowe wyrażenia tabel są tymczasowo nazwane zestawami wyników. Jest bliższe podzapytaniu pod względem zachowania i zakresu, ale jest jaśniejsze i bardziej modułowe. Ma też nazwę.
  • SQL CTE służy do uproszczenia kodu, a nie do przyspieszenia zapytania.
  • Siedem rzeczy, których się nauczyliśmy, będzie działać w SQL CTE.
  • Pięć rzeczy spowoduje błąd.
  • Po raz kolejny potwierdziliśmy, że IO STATYSTYKI i rzeczywisty plan wykonania zawsze dadzą Ci lepszy osąd. To prawda, jeśli porównasz CTE do podzapytania i partii przy użyciu tabel tymczasowych.

Podobał Ci się? Następnie przyciski mediów społecznościowych czekają na naciśnięcie. Wybierz swój ulubiony i podziel się miłością!

Czytaj także

Jak CTE może pomóc w pisaniu złożonych, potężnych zapytań:perspektywa wydajności


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak dodawać komentarze w SQL?

  2. Dogłębna eksploracja zabezpieczeń na poziomie wiersza

  3. Mit, że DROP i OBCIĄŻ TABELĘ nie są rejestrowane

  4. Modelowanie bazy danych do rejestracji sprzedaży. Część 1

  5. Poprawa rozwiązania mediany górnej/górnej opadającej