• Using Recursive Common table expressions to represent Tree structures


    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
    1   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


    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;
    
    
    
    

    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
    

    Trackbacks

    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
    Weblog: www.postgresonline.com
    Tracked: Aug 20, 00:58

    Comments
    Display comments as (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
  • 相关阅读:
    简单工厂模式、工厂模式、抽象工厂模式
    直接插入排序
    简单选择排序的陷阱
    面试3 题目二,不修改数组找到重复的数字
    二进制中1的个数(读不懂题目怎么办)
    用两个栈实现队列
    斐波那契数列
    替换空格
    python 实现杨辉三角(依旧遗留问题)
    递归实现二分查找
  • 原文地址:https://www.cnblogs.com/kungfupanda/p/5625709.html
Copyright © 2020-2023  润新知