but we'd have needed two levels of nested sub-SELECTs. It's a bit
easier to follow this way.
+
+
+
+
Recursive Queries
+
+
Search Order
+
+ When computing a tree traversal using a recursive query, you might want to
+ order the results in either depth-first or breadth-first order. This can
+ be done by computing an ordering column alongside the other data columns
+ and using that to sort the results at the end. Note that this does not
+ actually control in which order the query evaluation visits the rows; that
+ is as always in SQL implementation-dependent. This approach merely
+ provides a convenient way to order the results afterwards.
+
+
+ To create a depth-first order, we compute for each result row an array of
+ rows that we have visited so far. For example, consider the following
+ query that searches a table tree using a
+ link field:
+
+WITH RECURSIVE search_tree(id, link, data) AS (
+ SELECT t.id, t.link, t.data
+ FROM tree t
+ UNION ALL
+ SELECT t.id, t.link, t.data
+ FROM tree t, search_tree st
+ WHERE t.id = st.link
+)
+SELECT * FROM search_tree;
+
+
+ To add depth-first ordering information, you can write this:
+
+WITH RECURSIVE search_tree(id, link, data, path) AS (
+ SELECT t.id, t.link, t.data, ARRAY[t.id]
+ FROM tree t
+ UNION ALL
+ SELECT t.id, t.link, t.data, path || t.id
+ FROM tree t, search_tree st
+ WHERE t.id = st.link
+)
+SELECT * FROM search_tree ORDER BY path;
+
+
+
+ In the general case where more than one field needs to be used to identify
+ a row, use an array of rows. For example, if we needed to track fields
+ f1 and f2:
+
+WITH RECURSIVE search_tree(id, link, data, path) AS (
+ SELECT t.id, t.link, t.data, ARRAY[ROW(t.f1, t.f2)]
+ FROM tree t
+ UNION ALL
+ SELECT t.id, t.link, t.data, path || ROW(t.f1, t.f2)
+ FROM tree t, search_tree st
+ WHERE t.id = st.link
+)
+SELECT * FROM search_tree ORDER BY path;
+
+
+
+
+ The queries shown in this and the following section involving
+ ROW constructors in the target list only support
+ UNION ALL (not plain UNION) in the
+ current implementation.
+
+
+
+
+ Omit the ROW() syntax in the common case where only one
+ field needs to be tracked. This allows a simple array rather than a
+ composite-type array to be used, gaining efficiency.
+
+
+
+ To create a breadth-first order, you can add a column that tracks the depth
+ of the search, for example:
+
+WITH RECURSIVE search_tree(id, link, data, depth) AS (
+ SELECT t.id, t.link, t.data, 0
+ FROM tree t
+ UNION ALL
+ SELECT t.id, t.link, t.data, depth + 1
+ FROM tree t, search_tree st
+ WHERE t.id = st.link
+)
+SELECT * FROM search_tree ORDER BY depth;
+
+
+ To get a stable sort, add data columns as secondary sorting columns.
+
+
+
+ The recursive query evaluation algorithm produces its output in
+ breadth-first search order. However, this is an implementation detail and
+ it is perhaps unsound to rely on it. The order of the rows within each
+ level is certainly undefined, so some explicit ordering might be desired
+ in any case.
+
+
+
+
+
+
Cycle Detection
+
When working with recursive queries it is important to be sure that
the recursive part of the query will eventually return no tuples,
cycle does not involve output rows that are completely duplicate: it may be
necessary to check just one or a few fields to see if the same point has
been reached before. The standard method for handling such situations is
- to compute an array of the already-visited values. For example, consider
+ to compute an array of the already-visited values. For example, consider again
the following query that searches a table graph using a
link field:
WITH RECURSIVE search_graph(id, link, data, depth) AS (
- SELECT g.id, g.link, g.data, 1
+ SELECT g.id, g.link, g.data, 0
FROM graph g
UNION ALL
SELECT g.id, g.link, g.data, sg.depth + 1
is_cycle and path to the loop-prone query:
-WITH RECURSIVE search_graph(id, link, data, depth, is_cycle, path) AS (
- SELECT g.id, g.link, g.data, 1,
- false,
- ARRAY[g.id]
+WITH RECURSIVE search_graph(id, link, data, depth, is_cycle, path) AS (
+ SELECT g.id, g.link, g.data, 0,
+ false,
+ ARRAY[g.id]
FROM graph g
UNION ALL
SELECT g.id, g.link, g.data, sg.depth + 1,
- g.id = ANY(path),
- path || g.id
+ g.id = ANY(path),
+ path || g.id
FROM graph g, search_graph sg
- WHERE g.id = sg.link AND NOT is_cycle
+ WHERE g.id = sg.link AND NOT is_cycle
)
SELECT * FROM search_graph;
compare fields f1 and f2:
-WITH RECURSIVE search_graph(id, link, data, depth, is_cycle, path) AS (
- SELECT g.id, g.link, g.data, 1,
- false,
- ARRAY[ROW(g.f1, g.f2)]
+WITH RECURSIVE search_graph(id, link, data, depth, is_cycle, path) AS (
+ SELECT g.id, g.link, g.data, 0,
+ false,
+ ARRAY[ROW(g.f1, g.f2)]
FROM graph g
UNION ALL
SELECT g.id, g.link, g.data, sg.depth + 1,
- ROW(g.f1, g.f2) = ANY(path),
- path || ROW(g.f1, g.f2)
+ ROW(g.f1, g.f2) = ANY(path),
+ path || ROW(g.f1, g.f2)
FROM graph g, search_graph sg
- WHERE g.id = sg.link AND NOT is_cycle
+ WHERE g.id = sg.link AND NOT is_cycle
)
SELECT * FROM search_graph;
- The recursive query evaluation algorithm produces its output in
- breadth-first search order. You can display the results in depth-first
- search order by making the outer query ORDER BY a
- path
column constructed in this way.
+ The cycle path column is computed in the same way as the depth-first
+ ordering column show in the previous section.
UNION ALL
SELECT n+1 FROM t
)
-SELECT n FROM t LIMIT 100;
+SELECT n FROM t LIMIT 100;
This works because
PostgreSQL's implementation
outer query will usually try to fetch all of the WITH query's
output anyway.
+
+
+
+
+
Common Table Expression Materialization
A useful property of WITH queries is that they are