您的位置:首页 > 其它

Using Recursive Common table expressions to represent Tree structures

2016-06-29 09:28 513 查看
http://www.postgresonline.com/journal/archives/131-Using-Recursive-Common-table-expressions-to-represent-Tree-structures.html

Tree Problem and was based on PostgreSQL 7.4 technology.

We'll repeat the text here for completeness and demonstrate the PostgreSQL 8.4 that solves this and more efficiently.

The Problem

Suppose you are tracking supplies and have a field called si_item and
another called si_parentid.
The parent keeps track of what subclass a supply item belongs to. E.g.
you have paper parent that has subclasses such as recycled,
non-recycled. When someone takes supplies, you want to return the fully
qualified name e.g. Paper->Recycled->20 Lb

Below is what the structure of your table looks like.

si_id int, si_parentid int, si_item. In your table are the following entries
si_idsi_parentidsi_item
1Paper
21Recycled
3220 lb
4240 lb
51Non-Recycled
6520 lb
7540 lb
85Scraps
Solution
CREATE TABLE supplyitem(si_id integer PRIMARY KEY, si_parentid integer, si_item varchar(100));

--load up the table (multirow constructor introduced in 8.2)
INSERT INTO supplyitem(si_id,si_parentid, si_item)
VALUES (1, NULL, 'Paper'),
(2,1, 'Recycled'),
(3,2, '20 lb'),
(4,2, '40 lb'),
(5,1, 'Non-Recycled'),
(6,5, '20 lb'),
(7,5, '40 lb'),
(8,5, 'Scraps');

--Recursive query (introduced in 8.4 returns fully qualified name)
WITH RECURSIVE supplytree AS
(SELECT si_id, si_item, si_parentid, CAST(si_item As varchar(1000)) As si_item_fullname
FROM supplyitem
WHERE si_parentid IS NULL
UNION ALL
SELECT si.si_id,si.si_item,
si.si_parentid,
CAST(sp.si_item_fullname || '->' || si.si_item As varchar(1000)) As si_item_fullname
FROM supplyitem As si
INNER JOIN supplytree AS sp
ON (si.si_parentid = sp.si_id)
)
SELECT si_id, si_item_fullname
FROM supplytree
ORDER BY si_item_fullname;

[/code]
Result looks like

si_id |      si_item_fullname
------+-----------------------------
1    | Paper
5    | Paper->Non-Recycled
6    | Paper->Non-Recycled->20 lb
7    | Paper->Non-Recycled->40 lb
8    | Paper->Non-Recycled->Scraps
2    | Paper->Recycled
3    | Paper->Recycled->20 lb
4    | Paper->Recycled->40 lb


Posted by Leo Hsu and Regina Obe in 8.4, basics, cte, intermediate at 13:44 | Comments (8) | Trackback (1)
Defined tags for this entry: common table expressions
Related entries by tags:

Using HStore for Archiving

Using wget directly from PostgreSQL using COPY FROM PROGRAM

Saying Happy Valentine in PostGIS

Foreign Data Wrap (FDW) Text Array, hstore, and Jagged Arrays

Finding contiguous primary keys

String Aggregation in PostgreSQL, SQL Server, and MySQL

Using LTree to Represent and Query Hierarchy and Tree Structures

Allocating People into Groups with SQL the Sequel

PostgresQL 8.4: Common Table Expressions (CTE), performance improvement, precalculated functions revisited

Trackbacks

Trackback specific URI for this entry

Social comments and analytics for this post

This post was mentioned on Twitter by roblb: Using Recursive
Common table expressions to represent Tree structures: A very long time
ago, we wrote .. http://bit.ly/Flne3 #postgres

Weblog: uberVU - social comments

Tracked: Jan 04, 21:19

PingBack

Weblog: www.postgresonline.com

Tracked: Aug 20, 00:58

Comments
Display comments as
(=linear#comments]Linear | Threaded)

Great topic!

A couple of observations:

* Unless the length 1000 has some significance, use TEXT instead of

VARCHAR(1000).

* It might well be both faster and more correct to push items into an array

and use array_to_string() in the outer SELECT, and it won't be subject to

sorting anomalies.

WITH RECURSIVE supplytree AS

(

SELECT

si_id,

si_item,

si_parentid,

ARRAY[si_item] AS si_item_array

FROM supplyitem

WHERE si_parentid IS NULL

UNION ALL

SELECT

si.si_id,si.si_item,

si.si_parentid,

sp.si_item_array || si.si_item As si_item_array

FROM

supplyitem As si

JOIN

supplytree AS sp

ON (si.si_parentid = sp.si_id)

)

SELECT

si_id,

array_to_string(si_item_array, '->') AS si_item_fullname

FROM supplytree

ORDER BY si_item_array;

#1

David Fetter

(Homepage)
on
2009-08-16 19:10

Have thought about using ltree ?
http://www.postgresql.org/docs/current/static/ltree.html
I'am not saying than WITH RECURSIVE is bad .. just that, there are simpler solution sometimes ;-)

#2

Arek

on
2009-09-19 18:16

Good point. We haven't explored the use of ltree so
will have to give it a test drive sometime. I think the only thing
against it is that its a PostgreSQL specific feature where as the CTE is
more ANSI portable (except for possiblyt the word RECURSIVE)

#2.1

Leo

on
2009-09-28 02:58

How do you use it to find the parent path for just a single item?

#3

sabra

on
2009-09-26 18:35

Sabra,

Couple of ways -- you could write a function as we demonstrated in
linked article, but that is not as suitable for multiple sets since it
would probably do a subquery for each record.

You coulde also take our example and limit with a WHERE clause but that is much slower than it could be.

The other way would be to recurse backward from the child to the parent.
So instead of starting at parent nodes -- you start at the child node
and keep on unioning until you hit a parent with no parent. Will have
to write that up sometime.

#3.1

Leo

on
2009-09-28 03:05

many thanks for this great example.i implemented the child to parent recursion in case someone needs it:

--Recursive query (introduced in 8.4 returns fully qualified name)

WITH RECURSIVE supplytree AS

(SELECT si_id, si_item, si_parentid, CAST(si_item As varchar(1000)) As si_item_fullname

FROM supplyitem

WHERE si_item in( '40 lb')

UNION ALL

SELECT si.si_id,si.si_item,

si.si_parentid,

CAST(si.si_item || '->' || sp.si_item_fullname As varchar(1000)) As si_item_fullname

FROM supplyitem As si

INNER JOIN supplytree AS sp

ON (si.si_id = sp.si_parentid)

)

SELECT si_id, si_item_fullname

FROM supplytree where si_parentid is null

ORDER BY si_item_fullname;

#4

krishnen

on
2010-02-16 15:24

Great example for recursive CTE. Very useful. Thanks!

#5

Shirish

on
2010-09-30 11:05

this is most easy

table tema

-field tema_id (is the identificator)

-field nombre (is the name)

-field padre_id (is the parent id)

WITH RECURSIVE tema_tree AS (

SELECT tema_id, nombre, padre_id, nombre||'' full_name

FROM tema

WHERE padre_id IS NULL

UNION ALL

SELECT t.tema_id, t.nombre, t.padre_id, tt.full_name||' -> '||t.nombre full_name

FROM tema t

JOIN tema_tree tt ON t.padre_id = tt.tema_id

)

SELECT tema_id, full_name

FROM tema_tree

ORDER BY 2

#6

vakan

(Homepage)
on
2011-01-13 16:14
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: