Struktur organisasi Toko Maju Jaya kini bertingkat: Yusuf melapor ke Dewi, Dewi melapor ke Andi, Andi melapor ke Siti, dan Siti melapor ke Budi. Pertanyaan sederhana seperti “siapa saja seluruh bawahan Siti, berapa tingkat kepemimpinan di bawahnya?” tiba-tiba sulit dijawab query biasa. Chapter Self-Join
bisa merangkai dua-tiga lapis join manual, tetapi jumlah lapisan harus diketahui lebih dulu — dan hierarki dunia nyata berubah tanpa memberi tahu query kita. SQL menutup celah ini dengan bentuk paling tidak terduga: sebuah CTE yang boleh membaca dirinya sendiri. WITH RECURSIVE membuat query yang hasilnya menjadi masukan bagi iterasi berikutnya — satu definisi, kedalaman berapa pun. Chapter ini membangunnya dari deret bilangan, memakainya untuk pohon karyawan, lalu menutup dengan pengaman agar rekursi tidak berubah menjadi putaran tanpa ujung.
Tabel Latihan: karyawan dan atasan_id #
Data latihan memakai satu tabel karyawan dengan kolom atasan_id yang menunjuk ke baris lain di tabel yang sama — pola self-referencing foreign key yang sudah diperkenalkan di chapter Self-Join
. Nilai NULL pada atasan_id menandai puncak hierarki. Pohon yang terbentuk sengaja dibuat asimetris: cabang Andi turun lima tingkat sampai Yusuf, cabang Rina berhenti di tingkat ketiga:
-- Tabel latihan chapter Recursive CTE: struktur karyawan Toko Maju Jaya.
-- atasan_id menunjuk ke baris karyawan lain (self-referencing foreign key);
-- NULL berarti tidak punya atasan -- puncak hierarki.
CREATE TABLE karyawan (
id integer PRIMARY KEY,
nama text NOT NULL,
jabatan text NOT NULL,
atasan_id integer REFERENCES karyawan (id)
);
-- Pohon yang dibentuk data ini:
-- Budi (Direktur)
-- +- Siti (Manajer)
-- +- Andi (Supervisor)
-- | +- Dewi (Staf)
-- | | +- Yusuf (Magang)
-- | +- Ratna (Staf)
-- +- Rina (Staf)
INSERT INTO karyawan (id, nama, jabatan, atasan_id) VALUES
(1, 'Budi Santoso', 'Direktur', NULL),
(2, 'Siti Rahayu', 'Manajer', 1),
(3, 'Andi Wijaya', 'Supervisor', 2),
(4, 'Dewi Lestari', 'Staf', 3),
(5, 'Ratna Kusuma', 'Staf', 3),
(6, 'Yusuf Hakim', 'Magang', 4),
(7, 'Rina Marlina', 'Staf', 2);
SELECT id, nama, jabatan, atasan_id FROM karyawan ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 7
id | nama | jabatan | atasan_id
----+--------------+------------+-----------
1 | Budi Santoso | Direktur |
2 | Siti Rahayu | Manajer | 1
3 | Andi Wijaya | Supervisor | 2
4 | Dewi Lestari | Staf | 3
5 | Ratna Kusuma | Staf | 3
6 | Yusuf Hakim | Magang | 4
7 | Rina Marlina | Staf | 2
(7 rows)Struktur Dasar: Anchor Member dan Recursive Member #
Sebelum menyentuh pohon karyawan, mari lihat anatomi rekursi pada contoh paling murni: deret bilangan 1 sampai 10. WITH RECURSIVE menuntut dua bagian di dalam kurung yang dipisah UNION ALL — bagian awal (anchor member) memberikan baris pemula, bagian kedua (recursive member) memproduksi baris berikutnya dengan membaca hasil sejauh ini:
-- Recursive CTE paling sederhana: deret bilangan 1 sampai 10.
-- Struktur wajibnya: anchor member + UNION ALL + recursive member.
WITH RECURSIVE deret (n) AS (
SELECT 1 -- anchor member: titik awal
UNION ALL
SELECT n + 1 -- recursive member: langkah berikutnya
FROM deret
WHERE n < 10 -- syarat berhenti
)
SELECT n FROM deret ORDER BY n;
-- Untuk deret angka yang sesederhana ini, generate_series lebih ringkas
-- dan tidak perlu struktur WITH RECURSIVE sama sekali.
SELECT n FROM generate_series(1, 10) AS n ORDER BY n;
-- Rentang tanggal pun pekerjaan generate_series:
-- tujuh hari berturut-turut tanpa satu pun baris ditulis manual.
SELECT d::date AS tanggal
FROM generate_series(DATE '2026-01-05', DATE '2026-01-11', interval '1 day') AS d
ORDER BY d;Output yang Dihasilkan:
n
----
1
2
3
4
5
6
7
8
9
10
(10 rows)
n
----
1
2
3
4
5
6
7
8
9
10
(10 rows)
tanggal
------------
2026-01-05
2026-01-06
2026-01-07
2026-01-08
2026-01-09
2026-01-10
2026-01-11
(7 rows)Ketiga query menghasilkan deret yang bisa diprediksi penuh — 1 sampai 10, lalu tujuh tanggal dari 5 sampai 11 Januari 2026. Mari kita bedah bagian pentingnya:
WITH RECURSIVE deret (n) AS (...)— kataRECURSIVEmelonggarkan satu larangan: tanpa kata itu, CTE tidak boleh merujuk namanya sendiri; dengan kata itu, rujukanFROM deretdi dalam definisideretmenjadi legal.SELECT 1(anchor member) — dievaluasi tepat sekali di awal; hasilnya (satu baris berisi1) adalah bibit seluruh perhitungan.SELECT n + 1 FROM deret WHERE n < 10(recursive member) — membaca hasil sementaraderet, menaikkann, dan menyumbangkan barisnya kembali.WHERE n < 10adalah syarat berhenti: begitunmencapai 10, bagian ini tidak menghasilkan baris lagi dan perulangan berakhir.UNION ALL— operator yang menyatukan hasil anchor dan seluruh hasil iterasi.UNION ALLmempertahankan baris ganda; peranUNIONbiasa akan kita bahas di bagian pengaman.
Dua query terakhir mengajarkan kejujuran pemilihan alat: generate_series menghasilkan deret 1–10 dan rentang tanggal dengan satu pemanggilan, tanpa struktur rekursif apa pun. Gunakan recursive CTE ketika baris berikutnya benar-benar bergantung pada baris sebelumnya — seperti mencari anak dari orang yang baru saja ditemukan; untuk rentang beraturan (angka, tanggal), generate_series lebih pendek dan lebih murah.
Cara Kerja: Working Table yang Menyusut #
Kata “rekursif” memanggil bayangan query yang memanggil dirinya. PostgreSQL melakukan hal yang setara dengan cara iteratif, dan model mentalnya penting untuk dipahami sebelum kita menulis pohon karyawan. Server menjaga satu tabel kerja (working table): anchor member mengisinya pertama kali, recursive membacanya, baris hasilnya masuk ke hasil akhir sekaligus menjadi isi working table yang baru, dan putaran berhenti saat working table kosong:
Diagram di atas memuat seluruh mesinnya; sisa hanya memilih anchor dan recursive yang tepat. Untuk struktur organisasi, keduanya punya bentuk baku: anchor mengambil akar pohon (atasan_id IS NULL), recursive mencari anak dari baris yang sudah terkumpul:
-- Seluruh struktur organisasi dari puncak ke bawah,
-- lengkap dengan kolom level (kedalaman dari Direktur).
WITH RECURSIVE hierarki AS (
-- Anchor member: baris tanpa atasan -- akar pohon.
SELECT id, nama, jabatan, atasan_id, 1 AS level
FROM karyawan
WHERE atasan_id IS NULL
UNION ALL
-- Recursive member: bawahan langsung dari baris yang sudah terkumpul.
SELECT k.id, k.nama, k.jabatan, k.atasan_id, h.level + 1
FROM karyawan k
INNER JOIN hierarki h ON k.atasan_id = h.id
)
SELECT level, id, nama, jabatan, atasan_id
FROM hierarki
ORDER BY level, id;Output yang Dihasilkan:
level | id | nama | jabatan | atasan_id
-------+----+--------------+------------+-----------
1 | 1 | Budi Santoso | Direktur |
2 | 2 | Siti Rahayu | Manajer | 1
3 | 3 | Andi Wijaya | Supervisor | 2
3 | 7 | Rina Marlina | Staf | 2
4 | 4 | Dewi Lestari | Staf | 3
4 | 5 | Ratna Kusuma | Staf | 3
5 | 6 | Yusuf Hakim | Magang | 4
(7 rows)Mari kita ikuti jalannya working table baris demi baris:
- Iterasi 0 (anchor) —
WHERE atasan_id IS NULLmenangkap satu baris:Budi Santoso,level1. Working table berisi Budi. - Iterasi 1 — recursive member mencari karyawan dengan
atasan_id = 1:Siti Rahayu,level2. Working table kini berisi Siti. - Iterasi 2 — anak-anak Siti ditemukan:
Andi WijayadanRina Marlina, keduanyalevel3. Working table berisi keduanya. - Iterasi 3 dan 4 — anak Andi (
Dewi Lestari,Ratna Kusuma,level4) menyusul, lalu anak Dewi (Yusuf Hakim,level5).Rinatidak punya anak, jadi cabangnya berhenti diam-diam. - Iterasi 5 — tidak ada lagi karyawan yang belum terkumpul; recursive member menghasilkan nol baris, working table kosong, perulangan berakhir.
Hasil akhirnya tujuh baris — seluruh perusahaan — dengan level yang bertambah satu setiap turun satu lapis. Perhatikan pula aturan penulisan yang dipegang query ini: kolom hasil anchor dan recursive harus sejajar jumlah dan urutannya (kolom level dihitung sebagai 1 di anchor dan h.level + 1 di recursive), dan ORDER BY level, id memastikan output terurut penuh secara deterministik.
Urutan Pohon dengan Kolom path #
ORDER BY level menampilkan lapisan demi lapisan — rapi untuk laporan jumlah per tingkat, tetapi sulit melihat siapa anak siapa. Untuk itu kita simpan jalur lengkap dari akar ke tiap baris dalam satu kolom path, lalu urutkan berdasarkan kolom itu:
-- Kolom path menyimpan rantai nama dari akar sampai baris tersebut.
-- ORDER BY path menghasilkan urutan pohon (depth-first):
-- induk selalu tepat sebelum seluruh anak-anaknya.
WITH RECURSIVE pohon AS (
SELECT id, nama, jabatan, 1 AS level, nama AS path
FROM karyawan
WHERE atasan_id IS NULL
UNION ALL
SELECT k.id, k.nama, k.jabatan, p.level + 1, p.path || ' > ' || k.nama
FROM karyawan k
INNER JOIN pohon p ON k.atasan_id = p.id
)
SELECT repeat(' ', level - 1) || nama AS struktur,
level,
path
FROM pohon
ORDER BY path;Output yang Dihasilkan:
struktur | level | path
-----------------------------+-------+-----------------------------------------------------------------------
Budi Santoso | 1 | Budi Santoso
Siti Rahayu | 2 | Budi Santoso > Siti Rahayu
Andi Wijaya | 3 | Budi Santoso > Siti Rahayu > Andi Wijaya
Dewi Lestari | 4 | Budi Santoso > Siti Rahayu > Andi Wijaya > Dewi Lestari
Yusuf Hakim | 5 | Budi Santoso > Siti Rahayu > Andi Wijaya > Dewi Lestari > Yusuf Hakim
Ratna Kusuma | 4 | Budi Santoso > Siti Rahayu > Andi Wijaya > Ratna Kusuma
Rina Marlina | 3 | Budi Santoso > Siti Rahayu > Rina Marlina
(7 rows)Kunci ada di satu ekspresi: p.path || ' > ' || k.nama — anak mewarisi path ayahnya lalu menempelkan namanya sendiri, sehingga path Yusuf Hakim menjadi Budi Santoso > Siti Rahayu > Andi Wijaya > Dewi Lestari > Yusuf Hakim: bukti tertulis bahwa rantai lima tingkat itu benar-benar dilalui baris demi baris. Karena tiap path diawali path induknya, ORDER BY path mengelompokkan seluruh sub-pohon tepat setelah induknya — urutan depth-first. Kolom struktur memanfaatkannya untuk visual: repeat(' ', level - 1) || nama menambah indentasi empat spasi per tingkat, dan bagan organisasi yang terbaca seperti pohon muncul di kolom pertama. Pola path semacam ini juga bahan utama kueri bill of materials — melacak komponen suku cadang bersarang sampai kedalaman berapa pun.
Menelusuri ke Dua Arah #
Akar tidak harus Direktur, dan arah tidak harus ke bawah. Kedua kebebasan itu dikendalikan dari dua titik: anchor menentukan titik awal, sedangkan kondisi join di recursive member menentukan arah — join ke anak berarti turun, join ke atasan berarti naik:
-- Arah traversal ditentukan oleh bagaimana recursive member join:
-- turun (anak dari baris terkumpul) atau naik (atasan dari baris terkumpul).
-- TURUN: seluruh tim di bawah Siti Rahayu -- sub-pohon berakar di id 2.
WITH RECURSIVE tim AS (
SELECT id, nama, jabatan, 1 AS level
FROM karyawan
WHERE id = 2
UNION ALL
SELECT k.id, k.nama, k.jabatan, t.level + 1
FROM karyawan k
INNER JOIN tim t ON k.atasan_id = t.id
)
SELECT level, id, nama, jabatan FROM tim ORDER BY level, id;
-- NAIK: rantai atasan Yusuf Hakim, dari Yusuf sampai Direktur.
-- Join-nya dibalik: baris terkumpul mencari karyawan yang menjadi atasannya.
WITH RECURSIVE atasan AS (
SELECT id, nama, jabatan, atasan_id, 1 AS level
FROM karyawan
WHERE id = 6
UNION ALL
SELECT k.id, k.nama, k.jabatan, k.atasan_id, a.level + 1
FROM karyawan k
INNER JOIN atasan a ON k.id = a.atasan_id
)
SELECT level, id, nama, jabatan FROM atasan ORDER BY level;Output yang Dihasilkan:
level | id | nama | jabatan
-------+----+--------------+------------
1 | 2 | Siti Rahayu | Manajer
2 | 3 | Andi Wijaya | Supervisor
2 | 7 | Rina Marlina | Staf
3 | 4 | Dewi Lestari | Staf
3 | 5 | Ratna Kusuma | Staf
4 | 6 | Yusuf Hakim | Magang
(6 rows)
level | id | nama | jabatan
-------+----+--------------+------------
1 | 6 | Yusuf Hakim | Magang
2 | 4 | Dewi Lestari | Staf
3 | 3 | Andi Wijaya | Supervisor
4 | 2 | Siti Rahayu | Manajer
5 | 1 | Budi Santoso | Direktur
(5 rows)Query pertama mengganti anchor menjadi WHERE id = 2: Siti Rahayu jadi akar, dan enam baris timnya terkumpul — Andi, Rina, lalu Dewi, Ratna, sampai Yusuf di level 4. Budi tidak pernah muncul karena traversal hanya menjauh dari akar. Query kedua membalik join-nya: k.id = a.atasan_id berarti “cari baris karyawan yang menjadi atasan baris terkumpul”, sehingga rantai memanjat — Yusuf → Dewi → Andi → Siti → Budi — dan berhenti di Budi karena atasan_id NULL tidak pernah cocok dengan k.id mana pun. Perhatikan baik-baik bahwa hanya satu baris lahir per iterasi saat menanjak: pohon ke atas selalu berupa rantai tunggal, karena satu karyawan hanya punya satu atasan.
Pengaman: Batas Kedalaman dan UNION #
Rekursi berhenti ketika recursive member berhenti menghasilkan baris. Data pohon kita berhenti dengan sendirinya — finite dan tanpa siklus — tetapi data nyata tidak selalu sebaik itu: atasan_id yang saling menunjuk (A melapor ke B, B melapor ke A) membuat working table tidak pernah kosong dan query berputar selamanya. Dua pengaman pertama yang perlu Anda hafal:
-- Dua pengaman untuk rekursi yang tidak kunjung berhenti.
-- Pengaman kedalaman: WHERE di recursive member membatasi sampai level 3.
-- Baris level 3 tetap tampil, tetapi tidak lagi dikembangkan.
WITH RECURSIVE tim AS (
SELECT id, nama, jabatan, 1 AS level
FROM karyawan
WHERE atasan_id IS NULL
UNION ALL
SELECT k.id, k.nama, k.jabatan, t.level + 1
FROM karyawan k
INNER JOIN tim t ON k.atasan_id = t.id
WHERE t.level < 3
)
SELECT level, id, nama, jabatan FROM tim ORDER BY level, id;
-- UNION (tanpa ALL) membuang duplikat: baris yang sudah pernah muncul
-- tidak masuk lagi ke working table, sehingga perulangan berhenti lebih cepat.
-- Anchor sengaja memuat dua nilai (1 dan 2) supaya duplikatnya terlihat.
-- UNION ALL: hasil 7 baris -- angka 2, 3, 4 muncul dua kali.
WITH RECURSIVE deret (n) AS (
(SELECT 1 UNION ALL SELECT 2)
UNION ALL
SELECT n + 1 FROM deret WHERE n < 4
)
SELECT n FROM deret ORDER BY n;
-- UNION: hasil 4 baris -- duplikat dibuang, tiap angka tampil sekali.
WITH RECURSIVE deret (n) AS (
(SELECT 1 UNION ALL SELECT 2)
UNION
SELECT n + 1 FROM deret WHERE n < 4
)
SELECT n FROM deret ORDER BY n;Output yang Dihasilkan:
level | id | nama | jabatan
-------+----+--------------+------------
1 | 1 | Budi Santoso | Direktur
2 | 2 | Siti Rahayu | Manajer
3 | 3 | Andi Wijaya | Supervisor
3 | 7 | Rina Marlina | Staf
(4 rows)
n
---
1
2
2
3
3
4
4
(7 rows)
n
---
1
2
3
4
(4 rows)Pengaman pertama adalah WHERE t.level < 3 di recursive member: baris level 3 (Andi, Rina) tetap tampil di hasil, tetapi tidak lagi dicarikan anak, sehingga traversal berhenti di kedalaman tiga — empat baris, bukan tujuh. Teknik ini bekerja pada data apa pun, termasuk yang bersiklus, dan pas untuk kebutuhan seperti “tiga tingkat ke bawah sang manajer”. Pengaman kedua adalah mengganti UNION ALL dengan UNION: baris duplikat dibuang sebelum masuk working table, sehingga baris yang sudah pernah dihasilkan tidak pernah diproses dua kali. Query deret menunjukkan efeknya — anchor dua nilai (1 dan 2) membuat jalur 1→2→3→4 dan 2→3→4 bertabrakan: UNION ALL melaporkan 7 baris dengan angka 2, 3, 4 kembar, UNION memangkasnya menjadi 4 baris bersih dan menghentikan iterasi begitu tidak ada baris baru. Harganya nyata: setiap iterasi harus menyaring seluruh hasil sebelumnya untuk mencari kebaruan — biaya yang tidak Anda bayar pada UNION ALL.
Recursive CTE menuntaskan perangkat section ini: subquery klasik di tiga posisi, EXISTS untuk keberadaan, CTE bernama untuk keterbacaan, dan rekursi untuk hierarki. Satu pertanyaan penutup menunggu jawaban definitif: dari semua alat itu, kapan sebenarnya subquery unggul dibanding join — dan kapan keduanya cuma dua ejaan untuk satu rencana eksekusi? Chapter terakhir section ini mengadu keduanya: Subquery vs Join .
Referensi #
- WITH Queries (Common Table Expressions)
- Set Returning Functions, termasuk generate_series
- SELECT Reference
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
arah-traversal.sql
Unduh berkas-- Arah traversal ditentukan oleh bagaimana recursive member join:
-- turun (anak dari baris terkumpul) atau naik (atasan dari baris terkumpul).
-- TURUN: seluruh tim di bawah Siti Rahayu -- sub-pohon berakar di id 2.
WITH RECURSIVE tim AS (
SELECT id, nama, jabatan, 1 AS level
FROM karyawan
WHERE id = 2
UNION ALL
SELECT k.id, k.nama, k.jabatan, t.level + 1
FROM karyawan k
INNER JOIN tim t ON k.atasan_id = t.id
)
SELECT level, id, nama, jabatan FROM tim ORDER BY level, id;
-- NAIK: rantai atasan Yusuf Hakim, dari Yusuf sampai Direktur.
-- Join-nya dibalik: baris terkumpul mencari karyawan yang menjadi atasannya.
WITH RECURSIVE atasan AS (
SELECT id, nama, jabatan, atasan_id, 1 AS level
FROM karyawan
WHERE id = 6
UNION ALL
SELECT k.id, k.nama, k.jabatan, k.atasan_id, a.level + 1
FROM karyawan k
INNER JOIN atasan a ON k.id = a.atasan_id
)
SELECT level, id, nama, jabatan FROM atasan ORDER BY level;
buat-tabel.sql
Unduh berkas-- Tabel latihan chapter Recursive CTE: struktur karyawan Toko Maju Jaya.
-- atasan_id menunjuk ke baris karyawan lain (self-referencing foreign key);
-- NULL berarti tidak punya atasan -- puncak hierarki.
CREATE TABLE karyawan (
id integer PRIMARY KEY,
nama text NOT NULL,
jabatan text NOT NULL,
atasan_id integer REFERENCES karyawan (id)
);
-- Pohon yang dibentuk data ini:
-- Budi (Direktur)
-- +- Siti (Manajer)
-- +- Andi (Supervisor)
-- | +- Dewi (Staf)
-- | | +- Yusuf (Magang)
-- | +- Ratna (Staf)
-- +- Rina (Staf)
INSERT INTO karyawan (id, nama, jabatan, atasan_id) VALUES
(1, 'Budi Santoso', 'Direktur', NULL),
(2, 'Siti Rahayu', 'Manajer', 1),
(3, 'Andi Wijaya', 'Supervisor', 2),
(4, 'Dewi Lestari', 'Staf', 3),
(5, 'Ratna Kusuma', 'Staf', 3),
(6, 'Yusuf Hakim', 'Magang', 4),
(7, 'Rina Marlina', 'Staf', 2);
SELECT id, nama, jabatan, atasan_id FROM karyawan ORDER BY id;
deret-bilangan.sql
Unduh berkas-- Recursive CTE paling sederhana: deret bilangan 1 sampai 10.
-- Struktur wajibnya: anchor member + UNION ALL + recursive member.
WITH RECURSIVE deret (n) AS (
SELECT 1 -- anchor member: titik awal
UNION ALL
SELECT n + 1 -- recursive member: langkah berikutnya
FROM deret
WHERE n < 10 -- syarat berhenti
)
SELECT n FROM deret ORDER BY n;
-- Untuk deret angka yang sesederhana ini, generate_series lebih ringkas
-- dan tidak perlu struktur WITH RECURSIVE sama sekali.
SELECT n FROM generate_series(1, 10) AS n ORDER BY n;
-- Rentang tanggal pun pekerjaan generate_series:
-- tujuh hari berturut-turut tanpa satu pun baris ditulis manual.
SELECT d::date AS tanggal
FROM generate_series(DATE '2026-01-05', DATE '2026-01-11', interval '1 day') AS d
ORDER BY d;
hierarki-level.sql
Unduh berkas-- Seluruh struktur organisasi dari puncak ke bawah,
-- lengkap dengan kolom level (kedalaman dari Direktur).
WITH RECURSIVE hierarki AS (
-- Anchor member: baris tanpa atasan -- akar pohon.
SELECT id, nama, jabatan, atasan_id, 1 AS level
FROM karyawan
WHERE atasan_id IS NULL
UNION ALL
-- Recursive member: bawahan langsung dari baris yang sudah terkumpul.
SELECT k.id, k.nama, k.jabatan, k.atasan_id, h.level + 1
FROM karyawan k
INNER JOIN hierarki h ON k.atasan_id = h.id
)
SELECT level, id, nama, jabatan, atasan_id
FROM hierarki
ORDER BY level, id;
hierarki-path.sql
Unduh berkas-- Kolom path menyimpan rantai nama dari akar sampai baris tersebut.
-- ORDER BY path menghasilkan urutan pohon (depth-first):
-- induk selalu tepat sebelum seluruh anak-anaknya.
WITH RECURSIVE pohon AS (
SELECT id, nama, jabatan, 1 AS level, nama AS path
FROM karyawan
WHERE atasan_id IS NULL
UNION ALL
SELECT k.id, k.nama, k.jabatan, p.level + 1, p.path || ' > ' || k.nama
FROM karyawan k
INNER JOIN pohon p ON k.atasan_id = p.id
)
SELECT repeat(' ', level - 1) || nama AS struktur,
level,
path
FROM pohon
ORDER BY path;
pengaman-dedup.sql
Unduh berkas-- Dua pengaman untuk rekursi yang tidak kunjung berhenti.
-- Pengaman kedalaman: WHERE di recursive member membatasi sampai level 3.
-- Baris level 3 tetap tampil, tetapi tidak lagi dikembangkan.
WITH RECURSIVE tim AS (
SELECT id, nama, jabatan, 1 AS level
FROM karyawan
WHERE atasan_id IS NULL
UNION ALL
SELECT k.id, k.nama, k.jabatan, t.level + 1
FROM karyawan k
INNER JOIN tim t ON k.atasan_id = t.id
WHERE t.level < 3
)
SELECT level, id, nama, jabatan FROM tim ORDER BY level, id;
-- UNION (tanpa ALL) membuang duplikat: baris yang sudah pernah muncul
-- tidak masuk lagi ke working table, sehingga perulangan berhenti lebih cepat.
-- Anchor sengaja memuat dua nilai (1 dan 2) supaya duplikatnya terlihat.
-- UNION ALL: hasil 7 baris -- angka 2, 3, 4 muncul dua kali.
WITH RECURSIVE deret (n) AS (
(SELECT 1 UNION ALL SELECT 2)
UNION ALL
SELECT n + 1 FROM deret WHERE n < 4
)
SELECT n FROM deret ORDER BY n;
-- UNION: hasil 4 baris -- duplikat dibuang, tiap angka tampil sekali.
WITH RECURSIVE deret (n) AS (
(SELECT 1 UNION ALL SELECT 2)
UNION
SELECT n + 1 FROM deret WHERE n < 4
)
SELECT n FROM deret ORDER BY n;