Ini adalah penggunaan klasik dari ekspresi tabel umum rekursif sederhana (WITH RECURSIVE
), tersedia di PostgreSQL 8.4 dan yang lebih baru.
Ditunjukkan di sini:http://sqlfiddle.com/#!12/78e15/9
Diberikan contoh data sebagai SQL:
CREATE TABLE Table1
("ID1" text, "ID2" text)
;
INSERT INTO Table1
("ID1", "ID2")
VALUES
('vc1', 'vc2'),
('vc2', 'vc3'),
('vc3', 'vc4'),
('vc4', 'rc7')
;
Anda dapat menulis:
WITH RECURSIVE chain(from_id, to_id) AS (
SELECT NULL, 'vc2'
UNION
SELECT c.to_id, t."ID2"
FROM chain c
LEFT OUTER JOIN Table1 t ON (t."ID1" = to_id)
WHERE c.to_id IS NOT NULL
)
SELECT from_id FROM chain WHERE to_id IS NULL;
Apa yang dilakukan adalah menjalankan rantai secara iteratif, menambahkan setiap baris ke chain
tabel sebagai dari- dan ke-pointer. Ketika menemukan baris yang referensi 'ke' tidak ada, ia akan menambahkan referensi 'ke' nol untuk baris itu. Iterasi berikutnya akan melihat bahwa referensi 'ke' adalah null dan menghasilkan baris nol, yang menyebabkan iterasi berakhir.
Kueri luar kemudian mengambil baris yang telah ditentukan sebagai akhir rantai dengan memiliki to_id yang tidak ada.
Dibutuhkan sedikit usaha untuk memahami CTE rekursif. Hal-hal penting yang harus dipahami adalah:
-
Mereka mulai dengan output dari kueri awal, yang berulang kali mereka gabungkan dengan output dari "bagian rekursif" (kueri setelah
UNION
atauUNION ALL
) sampai bagian rekursif tidak menambahkan baris. Itu menghentikan iterasi. -
Mereka tidak benar-benar rekursif, lebih berulang, meskipun mereka bagus untuk hal-hal yang mungkin Anda gunakan untuk rekursi.
Jadi pada dasarnya Anda membuat tabel dalam satu lingkaran. Anda tidak dapat menghapus baris atau mengubahnya, hanya menambahkan baris baru, jadi Anda biasanya memerlukan kueri luar yang memfilter hasil untuk mendapatkan baris hasil yang Anda inginkan. Anda akan sering menambahkan kolom tambahan yang berisi data perantara yang Anda gunakan untuk melacak status iterasi, mengontrol kondisi berhenti, dll.
Ini dapat membantu untuk melihat hasil tanpa filter. Jika saya mengganti kueri ringkasan akhir dengan sederhana SELECT * FROM chain
Saya dapat melihat tabel yang telah dibuat:
from_id | to_id
---------+-------
| vc2
vc2 | vc3
vc3 | vc4
vc4 | rc7
rc7 |
(5 rows)
Baris pertama adalah baris titik awal yang ditambahkan secara manual, tempat Anda menentukan apa yang ingin Anda cari - dalam hal ini adalah vc2
. Setiap baris berikutnya ditambahkan oleh UNION
ed istilah rekursif, yang melakukan LEFT OUTER JOIN
pada hasil sebelumnya dan mengembalikan serangkaian baris baru yang memasangkan to_id
. sebelumnya (sekarang di from_id
kolom) ke to_id
berikutnya . Jika LEFT OUTER JOIN
tidak cocok maka to_id
akan menjadi null, menyebabkan pemanggilan berikutnya mengembalikan baris sekarang dan mengakhiri iterasi.
Karena kueri ini tidak mencoba menambahkan hanya terakhir baris setiap kali, itu sebenarnya mengulangi sedikit pekerjaan yang adil setiap iterasi. Untuk menghindarinya, Anda perlu menggunakan pendekatan yang lebih mirip dengan Gordon, tetapi filter tambahan pada bidang kedalaman sebelumnya saat Anda memindai tabel input, jadi Anda hanya bergabung dengan baris terbaru. Dalam praktiknya, hal ini biasanya tidak diperlukan, tetapi dapat menjadi masalah untuk kumpulan data yang sangat besar atau jika Anda tidak dapat membuat indeks yang sesuai.
Lebih banyak dapat dipelajari di dokumentasi PostgreSQL di CTE.