图形结构,使用存储过程查找父级,postgres

时间:2017-07-30 17:31:49

标签: stored-procedures graph

我是存储过程的新手。作为我们的应用程序的一部分,我们有一个数据库,其中一个表具有子父关系。给定一个id,我们应该能够找出id的最终父节点,遍历表中的子父链接。

sample data

输入1:10943, 输出1:8656

输入2:5005, 输出2:9151,9160

不同的可能性

一个id可能有多个最终父母, id可能没有父级

任何输入都将受到赞赏。提前谢谢。

2 个答案:

答案 0 :(得分:1)

树结构答案:

这完全取决于你的数据的流程,我的意思是,你的孩子和父母如何及时表现(插入,更新)以及表格有多大(数百,数千)

我们可以将这两种情况概括为:大表(数千+行)和小表(数百行)。在任何情况下,您都可以使用“结果”表来保存所有孩子的第一个父级。

小表

如果你的桌子不是一个大桌子,那么可以把所有的放在一个PL中。如果要实现“结果”表,请调用PL。

BIG TABLE

如果您的桌子很大(非常大),那么您必须使用触发器来更新“结果”表。而“结果”必须是Eager Materialized View。为了使它能够流畅地工作而不必每分钟等待几分钟。

这是一个关于如何在小桌子的情况下做到这一点的例子,如果它很大,它会是类似的,但你必须努力在触发器上使它工作正常。此示例仅以解释为目的显示DO格式的PL,您可以轻松更改PL:

-- YOUR EXAMPLE TABLE, without child with two parents
CREATE TEMP TABLE demo (child integer,parent integer);
INSERT INTO demo VALUES
(10943,6766),
(6766,9003),
(9003,8656),
(5005,6995),
(6995,9151),
(6996,9160);

-- DO, for inline PL
DO $$
DECLARE
-- Variables, note that for variable asignation its better if you use ':='
    fathers integer[] := NULL;
    father integer := 0;
    firstfather integer := 0;
    no_father boolean := 'f';
    sons integer[] := NULL;
    son integer := 0;
    row integer := 1;
    rows_length integer := 0;
BEGIN
-- Create "result" table for the example, the drop is in case you test many times the code
    DROP TABLE IF EXISTS result;
    CREATE TEMP TABLE result (sons integer,fathers integer);
    SELECT array(SELECT child FROM demo)
    INTO sons;
    rows_length := array_length(sons,1);
-- Code that gets the first father of a child
    LOOP
        RAISE NOTICE 'son: %',sons[row];
        son = sons[row];
        LOOP
            father := (SELECT parent FROM demo WHERE child=son);
            IF father IS NULL
            THEN
                no_father='f';
            ELSE
                no_father='t';
                fathers[row] := father;
                son = father;
            END IF;
            EXIT WHEN no_father='f';
        END LOOP;    
        RAISE NOTICE 'father: %',fathers[row];
-- INSERT in "result" son with its first father
        INSERT into result(sons,fathers) values(sons[row],fathers[row]);
        row := row+1;
        EXIT WHEN rows_length < row;
    END LOOP;
END $$;
-- Display "result"
SELECT * FROM result;

此代码生成下一个表:

Sons    Fathers
---------------
10943   8656
6766    8656
9003    8656
5005    9151
6995    9151
6996    9160

注意:您的表显示了一个有两个父亲的孩子(6995),这在树状结构中是不可能的,这个例子适用于每个孩子都有一个父母的树结构。

答案 1 :(得分:1)

使用公用表表达式(CTE)可以更容易地将其编写为查询而不是作为过程。

假设我们有这张表al_test

+-----------+----------+
| parent_id | child_id |
+-----------+----------+
| 10943     | 6766     |
+-----------+----------+
| 6766      | 9003     |
+-----------+----------+
| 9003      | 8656     |
+-----------+----------+
| 5005      | 6995     |
+-----------+----------+
| 6995      | 9151     |
+-----------+----------+
| 6995      | 9160     |
+-----------+----------+

例如(PostgreSQL 8.4+),获取节点5005的所有父节点:

WITH RECURSIVE al_tree AS (
  SELECT parent_id, child_id, 1 as depth
  FROM al_test WHERE child_id = 5005

  UNION ALL

  SELECT al_test.parent_id, al_test.child_id, al_tree.depth + 1
  FROM al_test, al_tree
    WHERE al_test.child_id = al_tree.parent_id
)

SELECT parent_id
FROM al_tree
    WHERE depth = (SELECT MAX(depth) FROM al_tree);

结果:

+-----------+
| parent_id |
+-----------+
| 9151      |
+-----------+
| 9160      |
+-----------+