设为首页 收藏本站
查看: 1028|回复: 0

[经验分享] PostgreSQL数据库中,使用存储过程实现自关联表的树形递归遍历。

[复制链接]
发表于 2016-11-21 07:06:37 | 显示全部楼层 |阅读模式
  本来想用上一篇文章
中说的方法,强行在mysql上实现的,无奈还是太麻烦,相当一部分SQL需要改造,由于是新产品,干脆来个釜底抽薪,换PostgreSQL得了!
  由于现在PostgreSQL用的人少,不想这么优秀的数据库被大家(尤其中国的程序员)忽视,把自己的一点心得贡献一下,希望能对PostgreSQL推广起点作用吧!
  废话不说,看代码吧(测试表、测试数据都包含了,你准备好psql环境,直接执行就能看到效果):
  另:编写的这四个函数,原则是尽量通用,因此把表名和自关联字段名作为参数传递进来。

-- postgresql 8.3
--
-- 测试用表和数据
--

DROP TABLE IF EXISTS test_tree;
CREATE TABLE test_tree (
id    BIGINT    NOT NULL    PRIMARY KEY ,
name    VARCHAR(64),
description    VARCHAR(2048),
parent_id    BIGINT    REFERENCES test_tree(id)
);
-- 01-02-04-10
--         -11
--      -05-07
--         -08
--   -03-06-09
--         -12
INSERT INTO test_tree VALUES (1, '名字1', '描述1', null );
INSERT INTO test_tree VALUES (2, '名字2', '描述2', 1 );
INSERT INTO test_tree VALUES (3, '名字3', '描述3', 1 );
INSERT INTO test_tree VALUES (4, '名字4', '描述4', 2 );
INSERT INTO test_tree VALUES (5, '名字5', '描述5', 2 );
INSERT INTO test_tree VALUES (6, '名字6', '描述6', 3 );
INSERT INTO test_tree VALUES (7, '名字7', '描述7', 5 );
INSERT INTO test_tree VALUES (8, '名字8', '描述8', 5 );
INSERT INTO test_tree VALUES (9, '名字9', '描述9', 6 );
INSERT INTO test_tree VALUES (10, '名字10', '描述10', 4 );
INSERT INTO test_tree VALUES (11, '名字11', '描述11', 4 );
INSERT INTO test_tree VALUES (12, '名字12', '描述12', 6 );
CREATE LANGUAGE PLPGSQL;
--
-- 自关联表的向下递归
--
DROP FUNCTION IF EXISTS all_progeny_pk(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT);
CREATE OR REPLACE FUNCTION all_progeny_pk(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) RETURNS SETOF BIGINT AS $PROC$
DECLARE
record_child RECORD; -- 直接子记录
record_childs_progeny RECORD; -- 直接子记录的后代
BEGIN
-- 遍历顺序:深度优先;输出顺序:父在前,子在后
FOR record_child IN EXECUTE $$SELECT t.$$ || pkFieldName || $$ AS pk FROM $$ || tableName || $$ AS t WHERE t.$$ || parentPkFieldName || $$ = '$$ || thisPk || $$' ORDER BY t.$$ || pkFieldName LOOP  
RETURN NEXT record_child.pk;
FOR record_childs_progeny IN SELECT * FROM all_progeny_pk(tableName, pkFieldName, parentPkFieldName, record_child.pk) AS pk LOOP
RETURN NEXT record_childs_progeny.pk;
END LOOP;
END LOOP;
RETURN;
END;
$PROC$ LANGUAGE PLPGSQL;
COMMENT ON FUNCTION all_progeny_pk(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) IS '查找某张自关联表中,某记录的所有子孙的主键(不包含自己)';
select * from all_progeny_pk('test_tree', 'id', 'parent_id', 1) AS pk;
DROP FUNCTION IF EXISTS all_progeny_pk_with_self(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT);
CREATE OR REPLACE FUNCTION all_progeny_pk_with_self(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) RETURNS SETOF BIGINT AS $PROC$
DECLARE
record_progeny RECORD;
BEGIN
RETURN NEXT thisPk;
FOR record_progeny IN SELECT * FROM all_progeny_pk(tableName, pkFieldName, parentPkFieldName, thisPk) AS pk LOOP
RETURN NEXT record_progeny.pk;
END LOOP;
RETURN;
END;
$PROC$ LANGUAGE PLPGSQL;
COMMENT ON FUNCTION all_progeny_pk_with_self(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) IS '查找某张自关联表中,某记录的所有子孙的主键(包含自己)';
select * from all_progeny_pk_with_self('test_tree', 'id', 'parent_id', 1) AS pk;
--
-- 自关联表的向上递归
--
DROP FUNCTION IF EXISTS all_ancestor_pk(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT);
CREATE OR REPLACE FUNCTION all_ancestor_pk(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) RETURNS SETOF BIGINT AS $PROC$
DECLARE
record_parent RECORD; -- 直接父记录
record_parents_ancestor RECORD; -- 直接父记录的祖先
BEGIN
-- 输出顺序:父在前,子在后
FOR record_parent IN EXECUTE $$SELECT t.$$ || parentPkFieldName || $$ AS pk FROM $$ || tableName || $$ AS t WHERE t.$$ || pkFieldName || $$ = '$$ || thisPk || $$' ORDER BY t.$$ || pkFieldName LOOP  
IF record_parent.pk IS NOT NULL THEN
FOR record_parents_ancestor IN SELECT * FROM all_ancestor_pk(tableName, pkFieldName, parentPkFieldName, record_parent.pk) AS pk LOOP
RETURN NEXT record_parents_ancestor.pk;
END LOOP;
RETURN NEXT record_parent.pk;
END IF;
END LOOP;
RETURN;
END;
$PROC$ LANGUAGE PLPGSQL;
COMMENT ON FUNCTION all_ancestor_pk(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) IS '查找某张自关联表中,某记录的所有祖先的主键(不包含自己)';
select * from all_ancestor_pk('test_tree', 'id', 'parent_id', 12) AS pk;
DROP FUNCTION IF EXISTS all_ancestor_pk_with_self(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT);
CREATE OR REPLACE FUNCTION all_ancestor_pk_with_self(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) RETURNS SETOF BIGINT AS $PROC$
DECLARE
record_ancestor RECORD;
BEGIN
FOR record_ancestor IN SELECT * FROM all_ancestor_pk(tableName, pkFieldName, parentPkFieldName, thisPk) AS pk LOOP
RETURN NEXT record_ancestor.pk;
END LOOP;
RETURN NEXT thisPk;
RETURN;
END;
$PROC$ LANGUAGE PLPGSQL;
COMMENT ON FUNCTION all_ancestor_pk_with_self(tableName VARCHAR, pkFieldName VARCHAR, parentPkFieldName VARCHAR, thisPk BIGINT) IS '查找某张自关联表中,某记录的所有祖先的主键(包含自己)';
select * from all_ancestor_pk_with_self('test_tree', 'id', 'parent_id', 12) AS pk;

   注意:调用函数时,可以使用 "select funciton();" 也可以写 "select * from function()" 在linux上无问题,windows下第一种报错。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-303094-1-1.html 上篇帖子: PostgreSQL用户创建删除及error while loading shared libraries解决方法 下篇帖子: Postgresql中新建表的时候,如何使用主键使用序列(sequence)的值进行自增长
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表