问题
Let's say I've got the following table structure:
| ID | ParentID | Name |
I'd like to write a recursive PostgreSQL function for getting all child nodes of a node ID passed to it as a parameter.
Here's my code so far (I only have one part of the function which gets all the children of the passed ID, and now I need the recursive part):
CREATE OR REPLACE FUNCTION GetAllChildren(IN NodeID INTEGER) RETURNS INTEGER AS $$
DECLARE
Crs CURSOR FOR SELECT ID, ParentID, Name FROM Tree WHERE ParentID=NodeID;
VarRow Tree%ROWTYPE;
BEGIN
OPEN Crs;
CREATE TEMPORARY TABLE TBL(
ID SERIAL,
ParentID INTEGER,
Name CHARACTER(100)
);
LOOP
FETCH Crs INTO VarRow;
IF VarRow IS NULL THEN
EXIT;
END IF;
INSERT INTO TBL(ID, ParentID, Name) VALUES(VarRow.ID, VarRow.ParentID, VarRow.Name);
END LOOP;
CLOSE Crs;
RETURN 0;
END;
$$ LANGUAGE plpgsql;
Perhaps the biggest problem is that I don't know where to save the output between the calls of the recursion.
If you haven't figured out so far, it's about the adjacency list, getting all the children of a node and printing them out to a table.
Does anyone have a solution?
回答1:
Yes. It is there in PostgreSQL wiki, which is first hit on google.
回答2:
For information, there are common table expressions in Postgres, which will probably help here:
http://www.postgresql.org/docs/current/static/queries-with.html
Adapting the example from the docs:
WITH RECURSIVE rec_tree(parent_id, node_id, data, depth) AS (
SELECT t.parent_id, t.node_id, t.data, 1
FROM tree t
UNION ALL
SELECT t.parent_id, t.node_id, t.data, rt.depth + 1
FROM tree t, rec_tree rt
WHERE t.parent_id = rt.node_id
)
SELECT * FROM rec_tree;
(See the docs for an example that prevents cycles for a graph.)
回答3:
PostgreSQL doesn't know local (procedure) limited temporary tables - your temp table is visible in all instances of called function, and it will be visible outside your function too - it has session visibility.
But PostgreSQL functions (PostgreSQL has no procedures) can returns table directly - so you don't need use auxiliary table for storing data
CREATE OR REPLACE FUNCTION children_by_parent(_parent_id int) RETURNS SETOF children AS $$ -- children is table name DECLARE r children; BEGIN FOR r IN SELECT * FROM children WHERE parent_id = _parent_id LOOP RETURN NEXT r; -- return node RETURN QUERY SELECT * FROM children_by_parent(r.id); -- return children END LOOP; RETURN; END; $$ LANGUAGE plpgsql STRICT;
This form is faster, because you don't fill any table (although temp table is usually in RAM only).
You don't need use a explicit cursor in PostgreSQL - statement FOR does all, it is shorter and more user friendly.
- the best solution is Denis idea - use CTE - recursive SQL.
来源:https://stackoverflow.com/questions/17306314/function-to-return-a-table-of-all-children-of-a-node