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.
- Podzapytanie – 4 logiczne odczyty (ZWYCIĘZCA!).
- SQL CTE – 6 logicznych odczytów.
- 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