Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Semua yang Perlu Anda Ketahui Tentang SQL CTE di Satu Tempat

Pertama kali Karl mendengar tentang SQL Server CTE adalah ketika dia sedang mencari sesuatu untuk membuat kode SQL-nya lebih mudah dilihat. Ini agak sakit kepala ketika Anda melihatnya. Anton, rekannya yang bersangkutan, menanyakan tentang CTE. Karl mengira Anton mengacu pada sakit kepalanya. Mungkin dia salah dengar, jadi dia menjawab, “Tentu saja tidak.” Lucunya, dia mengacu pada Chronic Traumatic Encephalopathy, juga CTE – penyakit neurodegeneratif yang disebabkan oleh cedera kepala berulang. Tetapi berdasarkan tanggapan Karl, Anton tahu pasti bahwa rekannya tidak mengerti apa yang dia katakan.

Sungguh cara yang gila untuk memperkenalkan CTE! Jadi, sebelum Anda masuk ke kapal yang sama, mari kita perjelas, apa itu SQL CTE atau Common Table Expressions di dunia SQL?

Anda dapat membaca dasar-dasarnya di sini. Sementara itu, kita akan belajar lebih banyak tentang apa yang terjadi dalam cerita yang tidak biasa ini.

4 Hal Dasar Tentang CTE di SQL Server

“Sebuah SQL CTE Memiliki Nama”

Anton memulai dengan gagasan bahwa SQL CTE untuk sementara dinamai kumpulan hasil. Sebagai sarana sementara, cakupan CTE terbatas.

"Jadi, ini seperti subquery?" tanya Karl.

“Di satu sisi, ya. Tapi subquery tidak bisa disebutkan,” kata Anton. “CTE memiliki nama seperti tabel dengan nama. Namun, alih-alih CREATE, Anda menggunakan WITH untuk membuatnya.” Kemudian, dia menulis sintaks di atas kertas:

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

“CTE Hilang Saat SELECT Selesai”

Anton melanjutkan dengan menjelaskan ruang lingkup SQL CTE.

“Tabel sementara bisa ada dalam ruang lingkup prosedur atau secara global. Tapi CTE hilang ketika SELECT selesai, ”katanya dengan sajak. “Sama saja jika digunakan untuk INSERT, UPDATE, atau DELETE,” lanjutnya.

“Anda Tidak Dapat Menggunakannya Kembali”

“Tidak seperti tampilan atau tabel sementara, Anda tidak dapat menggunakan kembali SQL CTE. Namanya ada di sana, jadi Anda bisa merujuknya di dalam dan di luar kueri. Tapi itu saja,” kata Anton.

“Jadi, apa hebatnya SQL CTE?” tanya Karl.

“Anda Dapat Membuat Kode Anda Lebih Mudah Dibaca”

"Masalah besar?" Anton membalas pertanyaan itu. “Anda dapat membuat kode Anda mudah dibaca. Bukankah itu yang Anda cari?”

“Itu benar,” Karl mengakui.

Jadi, apa langkah logis selanjutnya yang harus dilakukan Karl?

Hal Ekstra Tentang CTE di SQL

Keesokan harinya, Karl melanjutkan pencariannya untuk SQL CTE. Selain yang di atas, inilah yang dia temukan:

  • SQL CTE bisa non-rekursif atau rekursif.
  • Tidak hanya SQL Server tetapi juga MySQL dan Oracle mendukung ide tersebut. Faktanya, ini adalah bagian dari spesifikasi SQL-99.
  • Meskipun digunakan untuk menyederhanakan kode SQL, ini tidak meningkatkan kinerja.
  • Dan juga tidak akan menggantikan subquery dan tabel sementara. Masing-masing memiliki tempat dan kegunaannya.

Singkatnya, ini adalah cara lain untuk mengekspresikan kueri .

Tetapi Karl sangat menginginkan detail lebih lanjut, jadi dia terus mencari apa yang akan berhasil, apa yang tidak, dan bagaimana kinerjanya vs. subquery dan tabel sementara.

Apa yang Akan Berfungsi di SQL Server CTE?

Menggali lebih jauh untuk mengungkap lebih banyak tentang CTE, Karl mencantumkan di bawah apa yang akan diterima SQL Server. Intip juga studinya.

Tetapkan Alias ​​Sebaris atau Kolom Eksternal

SQL CTE mendukung dua bentuk pemberian alias kolom. Yang pertama adalah bentuk inline, seperti contoh di bawah ini:

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

Kode di atas menggunakan alias kolom dalam definisi CTE ketika ditugaskan dalam pernyataan SELECT. Apakah Anda memperhatikan COUNT(*) AS NumberOfOrders ? Itu formulir sebaris.

Sekarang, contoh lain adalah bentuk eksternal:

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

Kolom juga dapat didefinisikan dalam tanda kurung setelah menyetel nama CTE. Perhatikan WITH Sales_CTE (SalesPersonID, NumberOfOrders) .

CTE dalam SQL Mendahului SELECT, INSERT, UPDATE, atau DELETE

Item berikutnya adalah tentang mengkonsumsi CTE. Contoh pertama dan umum adalah ketika mendahului pernyataan 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

Apa yang ditunjukkan oleh contoh ini?

  • Penjualan_CTE – nama CTE.
  • (SalesPersonID, NumberOfOrders) – definisi kolom CTE.
  • PILIH SalesPersonID, COUNT(*) DARI Sales.SalesOrderHeader MANA SalesPersonID BUKAN NULL GROUP OLEH SalesPersonID – SELECT dalam yang mendefinisikan CTE.
  • PILIH a.SalesPersonID, CONCAT(P.LastName,’, ‘,P.FirstName,’ ‘,P.MiddleName) SEBAGAI SalesPerson – kueri luar yang menggunakan CTE. Contoh ini menggunakan SELECT untuk menggunakan CTE.
  • DARI Sales_CTE a – referensi kueri luar ke CTE.

Selain SELECT, ini juga berfungsi dengan INSERT, UPDATE, dan DELETE. Berikut ini contoh penggunaan 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

Dalam daftar di atas, CTE mengambil gaji terakhir untuk 16 karyawan. Kumpulan hasil CTE kemudian digunakan untuk memasukkan rekor baru di EmployeePayHistory . Karl mencatat temuannya dengan elegan. Juga, dia menggunakan contoh yang pas.

Tentukan Beberapa CTE dalam 1 Kueri

Betul sekali. Karl menemukan bahwa beberapa CTE dimungkinkan dalam 1 kueri. Ini contohnya:

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

Kode di atas menggunakan 2 CTE dalam satu query, yaitu LatestEmployeePay dan PreviousEmployeePay .

Lihat CTE Beberapa Kali

Ada lagi contoh sebelumnya. Perhatikan juga bahwa Anda dapat INNER JOIN CTE pertama ke CTE kedua. Akhirnya, kueri luar dapat menggabungkan kedua 2 CTE. Pembayaran Karyawan Terbaru telah dirujuk dua kali.

Meneruskan Argumen ke SQL CTE

Argumen, seperti variabel, dapat diteruskan bersama 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

Kode di atas dimulai dengan mendeklarasikan dan menyetel variabel @SalesPersonID . Nilai tersebut kemudian diteruskan ke CTE untuk menyaring hasilnya.

Gunakan dalam KURSOR

Kursor SQL dapat menggunakan pernyataan SELECT dan mengulang hasil. Juga, SQL CTE dapat digunakan dengannya:

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;

Gunakan Tabel Sementara dalam CTE Rekursif

CTE rekursif menggunakan anggota jangkar dan anggota rekursif dalam definisi CTE. Ini membantu mendapatkan hierarki dalam tabel. SQL CTE juga dapat menggunakan tabel sementara untuk tujuan ini. Lihat contoh di bawah ini:

-- 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 menjelaskan dengan membedah CTE ini. Begini caranya.

Anggota jangkar adalah pernyataan SELECT pertama dengan level kru nol (0):

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

Anggota jangkar ini mendapatkan simpul akar dari hierarki. Klausa WHERE menetapkan bahwa level root (HigherRankID IS NULL ).

Anggota rekursif yang akan mendapatkan node anak diekstraksi di bawah ini:

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

Ada juga OPTION (MAXRECURSION 2) digunakan dalam kueri luar. CTE rekursif dapat menjadi masalah ketika hasil loop tak terbatas dari kueri rekursif. MAXRECURSION 2 menghindari kekacauan ini – ini membatasi pengulangan hanya pada 2 rekursi.

Ini mengakhiri daftar Karl tentang apa yang akan berhasil. Namun, tidak semua yang kita pikirkan bisa berhasil. Bagian selanjutnya akan membahas temuan Karl tentang ini.

Apa yang Tidak Berfungsi di SQL CTE?

Di sini, kami memiliki daftar hal-hal yang akan menghasilkan kesalahan saat menggunakan SQL CTE.

Tidak Ada Titik Koma Sebelum SQL CTE

Jika ada pernyataan sebelum CTE, pernyataan itu harus diakhiri dengan titik koma. Klausa WITH dapat berfungsi untuk tujuan lain seperti dalam petunjuk tabel, dengan demikian, titik koma akan menghilangkan ambiguitas. Pernyataan di bawah ini akan menyebabkan kesalahan:

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

Para pemula yang dulunya tidak mengakhiri pernyataan dengan titik koma mengalami kesalahan ini:

Kolom Tanpa Nama

“Lupa menaruh kolom alias? Kemudian, Anda mengalami kesalahan lain. ” Karl mengatakan ini dalam makalahnya dan juga memberikan contoh kode yang saya bagikan di bawah ini:

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

Kemudian, lihat pesan kesalahannya:

Nama Kolom Duplikat

Kesalahan lain yang terkait dengan #2 di atas adalah menggunakan nama kolom yang sama di dalam CTE. Anda dapat melakukannya dalam pernyataan SELECT normal, tetapi tidak dengan CTE. Karl punya contoh lain:

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

ORDER BY Clause Tanpa TOP atau OFFSET-FETCH

SQL standar tidak mengizinkan ORDER BY dalam ekspresi tabel saat kami menggunakannya untuk menyortir kumpulan hasil. Namun, jika TOP atau OFFSET-FETCH digunakan, ORDER BY menjadi alat bantu penyaringan.

Berikut adalah contoh Karl menggunakan 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

Perhatikan bahwa ini adalah contoh yang sama yang kita miliki sebelumnya, tetapi kali ini, TOP tidak ditentukan. Periksa kesalahannya:

Jumlah Kolom Tidak Sama Dengan Definisi Daftar Kolom

Karl menggunakan contoh CTE rekursif yang sama, tetapi dia mengeluarkan kolom di anggota jangkar:

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;

Kode di atas menggunakan daftar tiga kolom dengan bentuk eksternal, tetapi anggota jangkar hanya memiliki 2 kolom. Itu tidak diperbolehkan. Membuat kesalahan seperti ini akan memicu kesalahan:

Hal Lain yang Tidak Diizinkan di CTE

Selain daftar di atas, berikut adalah beberapa temuan Karl yang memicu kesalahan jika Anda menggunakannya secara tidak sengaja dalam SQL CTE.

  • Menggunakan SELECT INTO, klausa OPTION dengan petunjuk kueri, dan menggunakan FOR BROWSE.
  • Berbeda data dan tipe di kolom anggota jangkar dibandingkan dengan kolom anggota rekursif.
  • Memiliki kata kunci berikut dalam anggota rekursif dari CTE rekursif:
    • ATAS
    • OUTER JOIN (Tapi INNER JOIN diperbolehkan)
    • GROUP BY dan HAVING
    • Subkueri
    • PILIH BERBEDA
  • Menggunakan agregasi Skalar.
  • Menggunakan subkueri dalam anggota rekursif.
  • Memiliki SQL CTE bersarang.

SQL CTE vs. Tabel Sementara vs. Subkueri

Terkadang, Anda dapat menulis ulang SQL CTE menggunakan subquery. Juga, kadang-kadang, Anda dapat memecah SQL CTE menggunakan tabel sementara untuk alasan kinerja. Seperti kueri lainnya, Anda perlu memeriksa Rencana Eksekusi Aktual dan STATISTICS IO untuk mengetahui opsi mana yang harus diambil. SQL CTE mungkin bersahabat dengan mata, tetapi jika Anda menemui hambatan kinerja, gunakan opsi lain . Tidak ada opsi yang lebih cepat dari yang lain.

Mari kita periksa tiga pertanyaan dari makalah Karl yang membawa hasil yang sama. Satu menggunakan SQL CTE, yang lain menggunakan subquery, dan yang ketiga menggunakan tabel sementara. Untuk mempermudah, Karl menggunakan kumpulan hasil kecil.

Kode dan Kumpulan Hasil

Ini dimulai dengan menggunakan beberapa 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

Yang berikutnya adalah subquery. Seperti yang Anda perhatikan, CTE terlihat modular dan dapat dibaca, tetapi subquery di bawah ini lebih pendek:

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 juga mencoba membaginya menjadi potongan-potongan kecil kode dan kemudian menggabungkan hasilnya menggunakan tabel sementara.

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

Lihat hasil tiga cara berikut untuk mendapatkan gaji saat ini dan sebelumnya untuk Karyawan 16. Keduanya sama:

Pembacaan Logis

Apa yang paling banyak menghabiskan sumber daya SQL Server? Mari kita lihat STATISTICS IO. Karl menggunakan statistikparser.com untuk memformat hasil dengan baik – baik untuk kita.

Gambar 7 menunjukkan pembacaan logis menggunakan CTE vs. menggunakan subquery:

Selanjutnya, lihat total pembacaan logis saat memecah kode menjadi potongan-potongan kecil menggunakan tabel sementara:

Jadi, apa yang menghabiskan lebih banyak sumber daya? Karl memberi peringkat untuk kejelasan.

  1. Subquery – 4 pembacaan logis (PEMENANG!).
  2. SQL CTE – 6 pembacaan logis.
  3. Tabel sementara – 8 pembacaan logis.

Dalam contoh ini, yang tercepat adalah subquery.

Rencana Eksekusi Sebenarnya

Untuk memahami pembacaan logis yang kami dapatkan dari STATISTICS IO, Karl juga memeriksa Rencana Eksekusi Aktual. Dia mulai dari CTE:

Beberapa hal yang dapat kita amati dari rencana ini:

  • Pembayaran Karyawan Terbaru CTE dievaluasi dua kali saat digunakan dalam kueri luar dan saat digabungkan ke PreviousEmployeePay . Jadi, kita melihat 2 node TOP untuk ini.
  • Kami melihat PreviousEmployeePay dievaluasi sekali.

Kemudian, lihat Rencana Eksekusi Aktual dari kueri dengan subkueri:

Ada beberapa hal yang jelas di sini:

  • Rencananya lebih sederhana.
  • Lebih mudah karena subquery untuk mendapatkan pembayaran terakhir dievaluasi hanya sekali.
  • Tidak heran jika pembacaan logis lebih sedikit dibandingkan dengan pembacaan logis kueri dengan CTE.

Akhirnya, inilah Rencana Eksekusi Aktual ketika Karl menggunakan tabel sementara:

Karena ini adalah kumpulan dari tiga pernyataan, kami juga melihat tiga diagram dalam rencana. Ketiganya sederhana, tetapi rencana kolektif tidak sesederhana rencana kueri dengan subkueri.

Statistik Waktu

Menggunakan solusi dbForge Studio untuk SQL Server, Anda dapat membandingkan statistik waktu di Query Profiler. Untuk itu, tahan tombol CTRL dan klik nama hasil setiap kueri dalam riwayat eksekusi:

Statistik waktu konsisten dengan pembacaan logis dan Rencana Eksekusi Aktual. Subquery berlari tercepat (88ms). Yang diikuti oleh CTE (199ms). Yang terakhir adalah penggunaan tabel sementara (536ms).

Jadi, apa yang telah kita pelajari dari Karl?

Dalam contoh khusus ini, kami melihat bahwa menggunakan subquery jauh lebih baik ketika kami menginginkan opsi tercepat. Namun, ini bisa menjadi cerita yang berbeda jika persyaratannya tidak seperti itu.

Selalu periksa STATISTICS IO dan Rencana Eksekusi Aktual untuk mengetahui teknik mana yang digunakan.

Bawa Pulang

Saya harap Anda tidak membenturkan kepala ke dinding untuk memahami apa itu CTE (Common Table Expressions). Jika tidak, Anda mungkin mendapatkan CTE (Chronic Traumatic Encephalopathy). Bercanda, apa yang kami ungkapkan?

  • Ekspresi Tabel Umum diberi nama sementara sebagai kumpulan hasil. Ini lebih dekat ke subquery dalam perilaku dan ruang lingkup tetapi lebih jelas dan lebih modular. Ia juga memiliki nama.
  • SQL CTE adalah untuk menyederhanakan kode, bukan untuk membuat kueri Anda lebih cepat.
  • Tujuh hal yang telah kita pelajari akan berhasil di SQL CTE.
  • Lima hal akan memicu kesalahan.
  • Kami juga telah mengonfirmasi sekali lagi bahwa STATISTICS IO dan Actual Execution Plan akan selalu memberi Anda penilaian yang lebih baik. Memang benar jika Anda membandingkan CTE dengan subquery dan batch menggunakan tabel sementara.

Menikmatinya? Kemudian tombol media sosial menunggu untuk ditekan. Pilih salah satu favorit Anda dan bagikan cinta!

Baca Juga

Bagaimana CTE Dapat Membantu Menulis Kueri yang Kompleks dan Kuat:Perspektif Kinerja


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. NoSQL:hidup tanpa skema

  2. Prosedur Tersimpan Khusus untuk Mendapatkan Status Cadangan Basis Data Terbaru

  3. Menghubungkan SQuirreL SQL ke Microsoft Excel

  4. Menghindari Pemecahan Masalah Performa Lutut

  5. Memigrasikan Cluster Cassandra Anda