This is a patch which allows PgSQL to make hierarchical queries a la Oracle do.
(c) Evgen Potemkin 2003,2004, < gppl at inbox dot ru >, entirely based on PostgreSQL (http://www.postgresql.org)
Patch itself distributed under GPL. No warranty of any kind is given, use it at your own risk.
INSTALLATION
tar xzf postgresql-7.x.tar.gz
patch -p0 < hier.diff
cd postgresql-7.x/
./configure
... so on
SYNOPSIS
#create table data (id int4, pnt int4, data varchar(20));
data:
+----+-----+-------------+----------+---------+ | id | pnt | data | c_id | c_pnt | +----+-----+-------------+----------+---------+ | 0 | 0 | root | top | | | 1 | 0 | (1 leaf l1) | leaf 1 | top | | 2 | 0 | (2 leaf l1) | leaf 2 | top | | 3 | 0 | (3 leaf l1) | leaf 3 | top | | 4 | 1 | (11 leaf l2)| leaf 21 | leaf 2 | | 5 | 1 | (12 leaf l2)| leaf 22 | leaf 2 | | 6 | 1 | (13 leaf l2)| leaf 23 | leaf 2 | | 7 | 3 | (31 leaf l2)| leaf 211 | leaf 21 | | 8 | 3 | (32 leaf l2)| leaf 212 | leaf 21 | +----+-----+-------------+----------+---------+
# SELECT * FROM data CONNECT BY PRIOR id = pnt START WITH id=0;
output:
id | pnt | data | _level_ ----+-----+-------------+--------- 0 | 0 | root | 1 1 | 0 | (1 leaf l1) | 2 4 | 1 | (11 leaf l2)| 3 5 | 1 | (12 leaf l2)| 3 6 | 1 | (13 leaf l2)| 3 2 | 0 | (2 leaf l1) | 2 3 | 0 | (3 leaf l1) | 2 7 | 3 | (31 leaf l2)| 3 8 | 3 | (32 leaf l2)| 3
# create sequence tseq;
# SELECT nextval('tseq') as _id , PRIOR nextval('tseq') as _pnt, c_id, c_pnt, _level_ as lvl
FROM data CONNECT BY PRIOR c_id = c_pnt START WITH c_id='top';
output:
_id | _pnt | c_id | c_pnt | lvl -----+------+----------+---------+---- 1 | | top | | 1 2 | 1 | leaf 1 | top | 2 3 | 1 | leaf 2 | top | 2 4 | 3 | leaf 21 | leaf 2 | 3 5 | 4 | leaf 211 | leaf 21 | 4 6 | 4 | leaf 212 | leaf 21 | 4 7 | 3 | leaf 22 | leaf 2 | 3 8 | 3 | leaf 23 | leaf 2 | 3 9 | 1 | leaf 3 | top | 2
DESCRIPTION
Hierarchical query.
Lets tree is like:
(root) / | \ / | \ (1 leaf l1) (2 leaf l1) (3 leaf l1) / | \ | \ / | \ | \ (11 leaf l2) (12 leaf l2) (13 leaf l2) (31 leaf l2) (32 leaf l2)
+------------+ |root | |(1 leaf l1) | |(11 leaf l2)| |(12 leaf l2)| |(13 leaf l2)| |(2 leaf l1) | |(3 leaf l1) | |(31 leaf l2)| |(32 leaf l2)| +------------+
REASON.
Often hierarchical queries are made by adding special field which stores a position of a row in the tree, and then make "SELECT ... ORDER BY 'hier_position_field'" over the table. disability of such approach is that we can't sort leaves in some custom order.
This patch is based on internal PG's query processing and havn't such disadvantage.
SYNTAX.
SELECT t_expr, PRIOR t_expr [ AS alias ], ... FROM ... [ WHERE condition ] hier_clause [ HAVING condition [, ...]] [ ORDER BY ... ] [ LIMIT ... ] [ OFFSET ... ] hier_clause: connect_clause start_with_clause | start_with_clause connect_clause | /* EMPTY */ ; connect_clause: CONNECT BY PRIOR c_expr qual_Op c_expr | CONNECT BY c_expr qual_Op PRIOR c_expr ; start_with_clause: START WITH a_expr ;
only rows which pass condition will be used
expression next to PRIOR is evaluated against parent tuple, another c_expr - against child tuple. qual_Op is an operator for comparation, if results of evaluation is equal then tuples will be connected.
qual_Op is not only '=', it may be any comparation operator for types of given expressions.
for cases with different parent and child expressions: operator's function is called op_func(child_expression, parent_expression).
for user-defined operators: operator's function must return 0 if values are equal, !0 otherwise.
EX.: CONNECT BY PRIOR id = pnt
rows will be connected by parent 'id' and child 'pnt' columns.
i.e. if in some row (A) column 'pnt' is equal to 'id' column of other row (B)
means that row A is a child of B.
rows for which expr is true will be treaten as root rows and other will be connected to them.
used to qualifying rows after building hierarchy. only which satisfy condition will be returned.
all childs of any parent will be ordered in choosen order. rows can't be ordered by _level_ within same select where they're connected, use SELECT ... FROM (SELECT ... CONNECT ...) p ORDER BY _level_ construction.
selects value of t_expr from parent row. Can be useful for generating IDs on the fly, and referencing to them. t_expr must be present in target list.
RESULTS.
query returns sets like discribed in synopsis. '_level_' is additional column which added only in case of hierarchical query. it means deep of row in the tree, starts from 1. from version 0.4 and since it can be selected in target list and aliased there. see 2nd example above.
Difference with Oracle's hierarchical queries: WHERE clause evaluated BEFORE connecting. for qualifying rows after they been connected use HAVING.
MISC.
Subqueries, views and cursors are supported.
initdb is required
Feedback is welcome:)
CHANGES.
v 0.5
v 0.4
v 0.3
v 0.2
v 0.1