Subquery adalah query yang bersarang di dalam query lain. CTE (common table expression) adalah query dalam yang sama, tetapi dipindahkan ke bagian atas statement dengan WITH dan diberi nama, sehingga query yang panjang terbaca sebagai rangkaian langkah. Contoh-contohnya berfungsi di PostgreSQL, MySQL, SQLite, dan SQL Server.
Cara menggunakan CTE dan subquery
Subquery ditulis di tempat ia digunakan: di FROM ia berperan sebagai tabel, dan di WHERE atau SELECT ia menyediakan sebuah nilai atau daftar. CTE didefinisikan lebih dulu, lalu digunakan seperti tabel oleh query berikutnya:
-- Subquery: written where it's used
SELECT columns FROM (SELECT ...) AS name;
-- CTE: named first, then used like a table
WITH name AS (SELECT ...)
SELECT columns FROM name;
SELECT n * 2 AS doubled FROM (SELECT 21 AS n) AS numbers;
-- Output: 42
WITH numbers AS (SELECT 21 AS n)
SELECT n * 2 AS doubled FROM numbers;
-- Output: 42
Pada tabel sungguhan, kedua bentuk ini mengembalikan baris yang sama. Berikut satu filter yang ditulis dengan kedua cara:
-- Input: people
-- first_name | last_name | job | favorite_food
-- -----------+-----------+----------+--------------
-- Matthew | Ortiz | Chef | cheese
-- Matt | Novak | Pilot | pizza
-- Mattie | Brooks | Engineer | cheese
-- Priya | Nair | Teacher | cheese
-- Subquery
SELECT *
FROM (SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese') AS cheese_lovers
WHERE first_name LIKE 'Matt%';
-- CTE
WITH cheese_lovers AS (
SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese'
)
SELECT * FROM cheese_lovers
WHERE first_name LIKE 'Matt%';
-- Output of both:
-- first_name | last_name | job
-- -----------+-----------+---------
-- Matthew | Ortiz | Chef
-- Mattie | Brooks | Engineer
Aturan yang perlu diketahui
-
CTE hanya berlaku untuk satu statement. Query tepat setelah
WITHbisa menggunakannya; statement berikutnya tidak bisa. Untuk menyimpan hasil bagi beberapa query, gunakan tabel sementara atau view. -
Beri nama pada subquery di
FROM, sepertiAS numbersdi atas. MySQL dan SQL Server menolaknya jika tanpa nama. - Subquery yang digunakan sebagai satu nilai harus mengembalikan satu baris:
SELECT (SELECT 1 UNION ALL SELECT 2) AS n;
-- ERROR: more than one row returned by a subquery used as an expression
-- (SQLite doesn't complain: it quietly uses the first row, 1)
- Kecepatannya biasanya sama. Database umumnya merencanakan CTE yang dipakai sekali dengan cara yang sama seperti subquery padanannya, jadi pilih saja yang lebih mudah dibaca.
Sintaks alternatif
CTE yang merujuk dirinya sendiri (lihat di bawah) ditulis dengan WITH RECURSIVE:
-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE counter AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM counter WHERE n < 3
)
SELECT n FROM counter;
-- Output:
-- n
-- -
-- 1
-- 2
-- 3
Di SQL Server, statement sebelum WITH harus diakhiri titik koma, itulah sebabnya kode T-SQL sering memulai CTE dengan ;WITH.
Kapan sebaiknya menggunakan CTE alih-alih subquery?
Subquery cukup untuk satu langkah kecil. Beralihlah ke CTE saat query memiliki beberapa langkah, membutuhkan hasil yang sama dua kali, atau harus merujuk dirinya sendiri.
Filter terhadap satu nilai hasil perhitungan
Jika query dalam hanya menghasilkan nilai atau daftar untuk dibandingkan, misalnya rata-rata total pesanan, subquery di WHERE adalah cara terpendek untuk menulisnya. CTE juga bisa, tetapi menambahkan nama yang tidak diperlukan.
Pecah query panjang menjadi langkah-langkah bernama
Jika query memiliki beberapa tahap (total per pelanggan, lalu beberapa teratas, lalu daftar akhir), beri nama pada setiap tahap. Tulis beberapa CTE setelah satu WITH, dipisahkan koma: masing-masing bisa menggunakan CTE sebelumnya, dan query terbaca dari atas ke bawah, bukan dari dalam ke luar.
Gunakan hasil yang sama lebih dari sekali
Sebuah statement bisa merujuk CTE sebanyak yang diperlukan, sehingga hasil yang Anda butuhkan dua kali (daftar total beserta rata-ratanya, atau setiap total dan total keseluruhan) cukup ditulis sekali. Dengan subquery, Anda harus menyalin seluruh query dalam.
Menelusuri hierarki
Hanya CTE yang bisa merujuk dirinya sendiri. CTE rekursif dimulai dari beberapa baris dan terus melakukan join kembali ke tabel sampai tidak ada baris baru, begitulah cara menelusuri bagan organisasi atau pohon kategori, maupun menghasilkan deret angka atau tanggal.
Contoh nyata CTE dan subquery
Produk dengan harga di atas rata-rata kategorinya
Subquery merujuk p.category dari query luar, sehingga dihitung untuk kategori masing-masing produk (correlated subquery):
-- Input: products
-- name | category | price
-- --------------+-----------+-------
-- Desk Lamp | Lighting | 24.99
-- Floor Lamp | Lighting | 89.00
-- Office Chair | Furniture | 149.00
-- Standing Desk | Furniture | 399.00
-- Bookshelf | Furniture | 120.00
SELECT name, category, price
FROM products p
WHERE price > (
SELECT AVG(price) FROM products WHERE category = p.category
)
ORDER BY category, name;
-- Output:
-- name | category | price
-- --------------+-----------+-------
-- Standing Desk | Furniture | 399.00
-- Floor Lamp | Lighting | 89.00
Produk terlaris di setiap kategori
Tiga langkah, masing-masing bernama: jumlahkan unit per produk, beri peringkat produk di setiap kategori, lalu ambil yang teratas:
-- Input: sales
-- id | product | category | units
-- ---+--------------+-----------+------
-- 1 | Desk Lamp | Lighting | 3
-- 2 | Floor Lamp | Lighting | 5
-- 3 | Desk Lamp | Lighting | 4
-- 4 | Office Chair | Furniture | 2
-- 5 | Bookshelf | Furniture | 6
WITH product_totals AS (
SELECT category, product, SUM(units) AS units
FROM sales
GROUP BY category, product
),
ranked AS (
SELECT category, product, units,
RANK() OVER (PARTITION BY category ORDER BY units DESC) AS place
FROM product_totals
)
SELECT category, product, units
FROM ranked
WHERE place = 1
ORDER BY category;
-- Output:
-- category | product | units
-- ----------+-----------+------
-- Furniture | Bookshelf | 6
-- Lighting | Desk Lamp | 7
Pangsa penjualan setiap wilayah
region_totals digunakan dua kali: sekali untuk baris-barisnya, dan sekali di dalam subquery untuk total keseluruhan:
-- Input: orders
-- id | region | amount
-- ---+--------+--------
-- 1 | North | 1200.00
-- 2 | South | 450.00
-- 3 | North | 300.00
-- 4 | West | 800.00
-- 5 | South | 250.00
WITH region_totals AS (
SELECT region, SUM(amount) AS total
FROM orders
GROUP BY region
)
SELECT
region,
total,
ROUND(100.0 * total / (SELECT SUM(total) FROM region_totals), 1) AS pct_of_sales
FROM region_totals
ORDER BY total DESC;
-- Output:
-- region | total | pct_of_sales
-- -------+---------+-------------
-- North | 1500.00 | 50.0
-- West | 800.00 | 26.7
-- South | 700.00 | 23.3
Jika ditulis dengan subquery, query GROUP BY akan muncul dua kali.
Rantai atasan seorang karyawan
Mulai dari satu karyawan dan ikuti manager_id ke atas sampai habis:
-- Input: employees
-- id | name | manager_id
-- ---+-----------+-----------
-- 1 | Maya Chen | NULL
-- 2 | Sam Ortiz | 1
-- 3 | Jo Kim | 2
-- 4 | Lee Ross | 2
-- 5 | Ana Diaz | 1
-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE chain AS (
SELECT id, name, manager_id, 0 AS level
FROM employees
WHERE name = 'Jo Kim'
UNION ALL
SELECT e.id, e.name, e.manager_id, chain.level + 1
FROM employees e
JOIN chain ON e.id = chain.manager_id
)
SELECT level, name FROM chain ORDER BY level;
-- Output:
-- level | name
-- ------+----------
-- 0 | Jo Kim
-- 1 | Sam Ortiz
-- 2 | Maya Chen
Bacaan lanjutan tentang CTE dan subquery
Di blog ini:
- Panduan SQL Temp Table: menyimpan hasil antara untuk lebih dari satu statement
-
Panduan Mencari Duplikat di SQL: CTE dengan
ROW_NUMBER()untuk menemukan baris berulang - Panduan Menghapus Duplikat di SQL: menghapus duplikat melalui CTE
- Panduan SQL GROUP BY: agregat yang menjadi dasar sebagian besar CTE
- Panduan Diagram SQL JOIN: melakukan join CTE ke tabel lain
Dokumentasi resmi:
- PostgreSQL: query WITH dan ekspresi subquery
- MySQL: WITH (common table expressions) dan subquery
- SQLite: klausa WITH
- SQL Server: WITH common_table_expression dan subquery
Beekeeper Studio Adalah GUI Database Gratis & Open Source
Tool query & editor SQL terbaik yang pernah saya pakai. Semua yang saya butuhkan untuk mengelola database ada di sini. - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studio cepat, intuitif, dan mudah digunakan. Beekeeper mendukung banyak sekali database, dan berjalan mulus di Windows, Mac, dan Linux.
Apa Kata Pengguna Tentang Beekeeper Studio
"Beekeeper Studio sepenuhnya menggantikan alur kerja SQL lama saya. Cepat, intuitif, dan membuat pekerjaan database kembali menyenangkan."
"Saya sudah mencoba banyak GUI database, tetapi Beekeeper punya keseimbangan sempurna antara fitur dan kesederhanaan. Langsung jalan."