`
zqhxuyuan
  • 浏览: 32256 次
  • 性别: Icon_minigender_1
  • 来自: 福建
社区版块
存档分类
最新评论

2011 项目笔记一. 数据库之向下递归取子组

阅读更多

项目中用户组,设备组采用树. 表结构是最简单的id+parentid来构造树.
功能点: 根据组id查询组下的所有子组.
此文首先给出最终版本,并分析如何实现.

--***** 数据库最终版本 *****--
CREATE OR REPLACE FUNCTION getchild_set(integer,integer) RETURNS SETOF integer AS
$BODY$
	/*
	根据组id查询所有子组.第二个参数为0:用户组; 为1:设备组
	实现方式一: 返回integer集合类型(组id列表)
	select getchild_set(1,0) as id
	*/
	DECLARE
		ret integer;
	BEGIN
		RETURN NEXT $1;
		IF $2 = 0 THEN
			FOR ret IN SELECT groupid FROM som_user_group_info WHERE parentid = $1 LOOP
				FOR ret IN select * from getchild_set(ret,0) LOOP
					RETURN NEXT ret;
				END LOOP;
			END LOOP;
		ELSE
			FOR ret IN SELECT id FROM som_device_group_info WHERE parentid = $1 LOOP
				FOR ret IN select * from getchild_set(ret,1) LOOP
					RETURN NEXT ret;
				END LOOP;
			END LOOP;
		END IF;
	END;
$BODY$
LANGUAGE 'plpgsql'

CREATE OR REPLACE FUNCTION getchild_str(integer,integer) RETURNS character varying AS
$BODY$
	/*
	根据组id查询所有子组.第二个参数为0:用户组; 为1:设备组
	实现方式2: 返回逗号分隔的字符串
	select getchild_str(1,0) as id
	*/
	declare
		ret character varying := '';
		tmp integer;
	begin
		IF $2 = 0 THEN
			for tmp in select groupid from som_user_group_info where parentid = $1 loop
				ret = ret || ',' || getchild_str(tmp,0);
			end loop;
		ELSE
			for tmp in select id from som_device_group_info where parentid = $1 loop
				ret = ret || ',' || getchild_str(tmp,1);
			end loop;
		END IF;
		return $1 || ret;	
	end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100;

 ------------------------------------------------------
postgretql 函数递归:

--①取得一棵树的所有孩子节点,包括自己.
WITH RECURSIVE r AS ( 
	SELECT * FROM tree WHERE id = 1 
	union   ALL 
	SELECT tree.* FROM tree, r WHERE tree.parent = r.id 
) 
SELECT * FROM r ORDER BY id; 
 
--根据组id 向下递归查找所有子组(也包括自己)--针对用户组的SQL 
WITH RECURSIVE r AS ( 
	SELECT * FROM som_user_group_info WHERE groupid = 1 
	UNION ALL 
	SELECT t.* FROM som_user_group_info t, r 
		WHERE t.parentid = r.groupid
)
SELECT * FROM r ORDER BY groupid

 但是这样只能写在sqlmap里或者直接在postgretql的控制台执行.不能写成类似函数的形式来复用.
不过如果要复用的话,写在一个统一的sqlmap里不是也可以,然后在需要的地方引用这个SQL片段.
问题又来了,因为后端也要用到这些东西,最好能在数据库层进行存储,而不是写在前端.

采用函数有2种形式:一是返回集合类型;二是返回字符串类型.

--③
CREATE OR REPLACE FUNCTION public.query_child_menus (integer) RETURNS SETOF public.menus AS
DECLARE
	itemid ALIAS FOR $1;
	itemrecord record;
BEGIN
	SELECT s.* INTO itemrecord FROM som_user_group_info s  where id=itemid;
	RETURN NEXT itemrecord;
	IF (select count(1) from public.menus s where s.parent_id=itemrecord.id) >0  THEN
		for itemrecord in SELECT s.* FROM public.menus s  where s.parent_id=itemrecord.id LOOP
			for itemrecord in select * from public.query_child_menus (itemrecord.id) LOOP
				RETURN NEXT itemrecord;
			end LOOP;
		end LOOP;
	END IF;
	RETURN;
END;
LANGUAGE 'plpgsql'

--返回Table集合类型:(som_user_group_info) 
--select * from getchild_table(1)
CREATE OR REPLACE FUNCTION getchild_table(integer) 
	RETURNS SETOF som_user_group_info AS
$BODY$
DECLARE
	ret record;
BEGIN
	select * into ret from som_user_group_info where groupid = $1;
	RETURN NEXT ret;
	IF (select count(1) from som_user_group_info where parentid = $1) > 0 THEN
		FOR ret IN
			select * from som_user_group_info where parentid = $1 
		LOOP
			FOR ret IN
				select * from getchild_table(ret.groupid)
			LOOP
				RETURN NEXT ret;
			END LOOP;
		END LOOP;
	END IF;
END;
$BODY$
LANGUAGE 'plpgsql'

--返回RECORD集合类型:(groupid,parentid,groupname) 
--select * from getchild_record(1) as foo(groupid integer,parentid integer,groupname varchar)
CREATE OR REPLACE FUNCTION getchild_record(integer) 
	RETURNS SETOF record AS
$BODY$
DECLARE
	ret record;
BEGIN
	select groupid,parentid,groupname into ret from som_user_group_info where groupid = $1;
	RETURN NEXT ret;
	FOR ret IN
		SELECT groupid,parentid,groupname FROM som_user_group_info WHERE parentid = $1 
	LOOP
		FOR ret IN
			select * from getchild_record(ret.groupid) as foo(groupid integer,parentid integer,groupname varchar)
		LOOP
			RETURN NEXT ret;
		END LOOP;
	END LOOP;
END;
$BODY$
LANGUAGE 'plpgsql'

--返回integer集合类型(groupid) usergroup版本
--select getchild_set(1)
CREATE OR REPLACE FUNCTION getchild_set(integer) 
	RETURNS SETOF integer AS
$BODY$
DECLARE
	ret integer;
BEGIN
	RETURN NEXT $1;
	FOR ret IN
		SELECT groupid FROM som_user_group_info WHERE parentid = $1 
	LOOP
		FOR ret IN
			select * from getchild_set(ret) 
		LOOP
			RETURN NEXT ret;
		END LOOP;
	END LOOP;
END;
$BODY$
LANGUAGE 'plpgsql'

--*****递归树设计*****--
CREATE OR REPLACE FUNCTION getchild_set(integer) 
	RETURNS SETOF integer AS
$BODY$
DECLARE
	ret integer;
	--ret1 integer;
BEGIN
	RETURN NEXT $1;
	FOR ret IN SELECT groupid FROM som_user_group_info WHERE parentid = $1 LOOP
		--步骤1: 仅查询节点的下一级节点:直接返回遍历到的节点的子节点
		--RETURN NEXT ret;
		
		--Error: query has no destination for retult data
		--select * from getchild_set(ret) 
		--RETURN NEXT ret;
		
		--步骤2: 对子节点递归调用自身
		FOR ret1 IN select * from getchild_set(ret) LOOP
			RETURN NEXT ret1;
		END LOOP;
		
		--步骤3: ret1可以统一用ret变量
		FOR ret IN select * from getchild_set(ret) LOOP
			RETURN NEXT ret;
		END LOOP;
	END LOOP;
END;
$BODY$
LANGUAGE 'plpgsql'

--返回字符串形式
--步骤1:
CREATE OR REPLACE FUNCTION recursive_child(integer) RETURNS character varying AS
$BODY$
	declare
		ret character varying := '';
		tmp integer;
	begin
		for tmp in select groupid from som_user_group_info where parentid = $1 loop
			--ret = ret || ',' || tmp;
			--ret = ret || recursive_child(tmp);
			ret = ret || ',' || tmp || recursive_child(tmp);
		end loop;
		return ret;
	end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100;  

--步骤2: 上面的函数返回的字符串形式如:,2,3,4 再加上自身id在为:1,2,3,4
CREATE OR REPLACE FUNCTION getchild_str(integer)
  RETURNS character varying AS
$BODY$
	declare
	begin
		return $1 || recursive_child($1);
	end;
$BODY$ LANGUAGE 'plpgsql' VOLATILE COST 100;  

--将上面2个函数合为一个,并加入类型
CREATE OR REPLACE FUNCTION getchild_str(integer) RETURNS character varying AS
$BODY$
	declare
		ret character varying := '';
		tmp integer;
	begin
		for tmp in select groupid from som_user_group_info where parentid = $1 loop
			ret = ret || ',' || getchild_str(tmp);
		end loop;
		return $1 || ret;	
	end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100;  
 

------------------------------------------------------EOF

参考资料:
① http://blog.csdn.net/wh62592855/article/details/6443909
② http://www.postgretql.org/docs/8.4/static/queries-with.html
③ http://it.chinawin.net/database/article-facc.html

0
0
分享到:
评论

相关推荐

    自考工学类计算机应用技术串讲笔记新版.doc

    3. 信息间的递归定义:信息和数据的关系是相互转化的,一次处理输出的信息可能成为下一次处理的原始数据,这种层次关系就形成了信息间的递归定义。 4. 信息反馈:控制系统的输入信息对受控对象产生作用后,将结果...

    算法图解读书笔记.pdf

    递归是函数自我调用的一种编程技术,通常用于解决可以分解为相似子问题的问题,但需要注意递归可能导致栈溢出等问题。 快速排序是高效的排序算法,采用分而治之的策略,将大问题分解为小问题来解决。快速排序在平均...

    数据结构(C语言描述)学习笔记.zip

    链表是由一系列节点组成,每个节点包含数据和指向下一个节点的指针。栈遵循“后进先出”(LIFO)原则,常用于递归和表达式求值。队列则遵循“先进先出”(FIFO)原则,适用于模拟各种等待队列,如打印机队列。 二、...

    net学习笔记及其他代码应用

    答:服务器端向客户端发送一个进程编号,一个程序域编号,以确定对象的位置。 24.在C#中,string str = null 与 string str = “” 请尽量使用文字或图象说明其中的区别。 答:string str = null 是不给他分配...

    数据结构与算法学习笔记.docx

    ### 数据结构与算法学习笔记 #### 一、数据的逻辑结构与存储结构 - **数据的逻辑结构**:表示数据元素之间的逻辑关系,是独立于计算机的数学模型。 - **线性结构**:每个元素至多有一个直接前驱和一个直接后继,...

    0854考研初试(自命题) 数据结构强化笔记 Akira37.pdf

    一种自平衡的树,用于数据库和文件系统的索引。 - **8.4.2 B+树的基本概念** 也是自平衡树,通常用于磁盘存储。 - **8.5 散列表** - **8.5.1 散列表的基本概念** 通过散列函数将键映射到数组索引上,以实现...

    技术笔记(第一部分整理)

    在处理多表关联的情况下,需要了解数据库设计中的关联关系,如一对一、一对多、多对多等。 ##### 部门表模块 部门表通常用于存储组织结构的信息。在实际开发中,可以通过实体类、DAO接口、服务接口等来实现对部门表...

    自考数据结构笔记

    - **单链表**:通过一系列节点表示,每个节点包含数据域和指向下一个节点的指针。 - **操作**:插入、删除、查找等基本操作。 #### 栈和队列 - **栈**:先进后出(FILO)的线性表,支持两种基本操作:入栈和出栈...

    Java基础笔记需要的可以看看.docx

    15. **向上与向下转型**:向上转型是将子类对象赋值给父类引用,向下转型则是将父类引用转换为子类对象,需要强制类型转换。 16. **动态方法绑定**:Java采用晚期绑定(或称为动态绑定),在运行时决定调用哪个方法...

    java学习笔记整理

    - **删除文件夹及内容**: 递归删除文件夹下的所有文件和子文件夹。 ##### 8.4 文件列表和文件过滤器 - **列出文件**: `new File("dir").listFiles()` - **文件过滤器**: 使用`FilenameFilter`或`FileFilter`接口...

    Java基础复习笔记10数据结构-排序二叉树

    - **查找方法**:从根节点开始,根据值大小向下查找直至找到目标节点。 - **删除方法**:根据节点的不同情况采取不同的删除策略。 #### 应用场景 排序二叉树在多种场景下都有应用,例如: 1. **数据库索引**:...

    php100学习笔记

    在删除非空目录时,可能需要递归删除其中的所有文件和子目录。 ### 类与对象 #### 构造函数与析构函数 - **`__construct()`**:构造函数在创建新对象时自动调用。可以通过构造函数初始化对象的属性。 - **`__...

    软考-软件设计师精华知识点笔记【第三章 数据结构】

    2. **链表**:链表是由节点(包含数据和指向下一个节点的指针)构成的数据结构。链表分为单链表、双链表和循环链表等,与数组相比,链表在插入和删除操作上更为灵活。 3. **栈**:栈是一种后进先出(LIFO)的数据...

    清华殷人昆数据结构笔记

    每个节点包含数据和指向下一个节点的指针,支持高效插入和删除。C++中,`std::list`和`std::forward_list`是链表的实现。 3. **栈与队列**:栈是后进先出(LIFO)的数据结构,常用于表达式求值和递归等;队列是先进...

    数据结构学习笔记(分章节描述)

    1. **数组**:数组是最基本的数据结构之一,它是一个有序的元素集合,每个元素都有一个唯一的索引。数组提供了直接访问任何位置元素的能力,但插入和删除操作通常效率较低。 2. **链表**:链表不同于数组,它的元素...

    传智播客视频JavaSE学习笔记

    使用递归算法遍历目录树,可以获取目录下的所有文件和子目录,适用于搜索文件、清理目录等任务。 #### 十一、Properties类 `Properties`类继承自`Hashtable`,用于处理键值对配置信息,常用于读写.properties配置...

    数据结构 课件 笔记【完美版】【初学者福音】

    5. **堆(Heap)**:堆是一种特殊的树形数据结构,满足最大堆(父节点大于子节点)或最小堆(父节点小于子节点)的性质。堆常用于实现优先队列,也是排序算法(如堆排序)的基础。 6. **树(Tree)**:树是一种非...

    18-DNS协议:网络世界的地址簿1

    此外,DNS还可以实现内部负载均衡,例如在一个应用访问数据库的场景下,配置数据库的域名而不是固定IP,当数据库需要更换服务器时,只需在DNS服务器上更新域名映射,无需改动应用配置。这种做法增强了系统的可维护性...

    php基础学习笔记中_5cbf5.doc

    `count()`函数用于计算数组中的元素数量,它可以接受一个可选参数,如果设置为1,则会递归计算嵌套数组的元素数量。 `array_unique()`函数则是用来移除数组中重复的值,返回一个新的不包含重复值的数组。此外,我们...

    SqlServer 2005 T-SQL Query 学习笔记(1)

    在SQL Server 2005中,T-SQL(Transact-SQL)是用于操作数据库的主要编程语言。本文主要探讨了T-SQL查询中的几个关键概念,包括`SELECT`语句、嵌套查询、多重引用、Common Table Expressions (CTE)以及如何使用CTE...

Global site tag (gtag.js) - Google Analytics