mysql查询树形结构上、下级

本文详细介绍了MySQL中的存储函数,包括FIND_IN_SET、GROUP_CONCAT和ROW_COUNT()等,以及它们在组织层级查询中的应用。通过存储函数,可以高效地查询子节点和父节点列表,适用于大规模组织结构数据的检索。然而,这种方法受限于字符串长度,可能在处理大量数据时遇到挑战。
摘要由CSDN通过智能技术生成

1、存储函数

find_in_set(str,strlist) :  查询字符串str是否在strlist存在,返回存在字符串str的个数。

  • str 要查询的字符串
  • strlist 字段名 参数以”,”分隔 如 (1,2,6,8)
  • 查询字段(strlist)中包含(str)的结果,返回结果为null或记录

假如字符串str在由N个子链组成的字符串列表strlist 中,则返回值的范围在 1 到 N 之间。 一个字符串列表就是一个由一些被 ‘,’ 符号分开的子链组成的字符串。

如果第一个参数是一个常数字符串,而第二个是type SET列,则FIND_IN_SET() 函数被优化,使用比特计算

如果str不在strlist 或strlist 为空字符串,则返回值为 0 。如任意一个参数为NULL,则返回值为 NULL。这个函数在第一个参数包含一个逗号(‘,’)时将无法正常运行。

group_concat([distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator '分隔符'] )

功能:将group by产生的同一个分组中的值连接起来,返回一个字符串结果。

说明:通过使用distinct可以排除重复值;如果希望对结果中的值进行排序,可以使用order by子句;separator是一个字符串值,缺省为一个逗号。

 ROW_COUNT() : 表示数据库中数据更新的条数。

if(express1,express2,express3)条件语句,if语句类似三元表达式,当exprss1成立时,执行express2,否则执行express3;

用存储函数,查询所有子节点列表,并拼接成字符串返回。

表结构、数据:

drop table if exists org_info;
create table org_info (
  ORG_ID varchar(60) not null,
  ORG_NAME varchar(60) not null,
  PARENT_ORG_ID varchar(60) default null,
  primary key (org_Id)
) engine=innodb default charset=utf8 ;

INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100000', '测试机构100000', '0'    );
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100001', '测试机构100001', '100000');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100002', '测试机构100002', '100001');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100003', '测试机构100003', '100002');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100004', '测试机构100004', '100003');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100005', '测试机构100005', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100006', '测试机构100006', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100007', '测试机构100007', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100008', '测试机构100008', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100009', '测试机构100009', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100010', '测试机构100010', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100011', '测试机构100011', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100012', '测试机构100012', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100013', '测试机构100013', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100014', '测试机构100014', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100015', '测试机构100015', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100016', '测试机构100016', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100017', '测试机构100017', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100018', '测试机构100018', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100019', '测试机构100019', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100020', '测试机构100020', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100021', '测试机构100021', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100022', '测试机构100022', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100023', '测试机构100023', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100024', '测试机构100024', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100025', '测试机构100025', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100026', '测试机构100026', '100004');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100027', '测试机构100027', '100002');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100028', '测试机构100028', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100029', '测试机构100029', '100028');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100030', '测试机构100030', '100028');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100031', '测试机构100031', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100032', '测试机构100032', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100033', '测试机构100033', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100034', '测试机构100034', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100035', '测试机构100035', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100036', '测试机构100036', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100037', '测试机构100037', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100038', '测试机构100038', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100039', '测试机构100039', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100040', '测试机构100040', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100041', '测试机构100041', '100027');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100042', '测试机构100042', '100031');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100043', '测试机构100043', '100032');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100044', '测试机构100044', '100033');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100045', '测试机构100045', '100034');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100046', '测试机构100046', '100035');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100047', '测试机构100047', '100036');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100048', '测试机构100048', '100037');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100049', '测试机构100049', '100038');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100050', '测试机构100050', '100039');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100051', '测试机构100051', '100040');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100052', '测试机构100052', '100041');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100053', '测试机构100053', '100041');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100054', '测试机构100054', '100002');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100055', '测试机构100055', '100054');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100056', '测试机构100056', '100055');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100057', '测试机构100057', '100055');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100058', '测试机构100058', '100054');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100059', '测试机构100059', '100058');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100060', '测试机构100060', '100002');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100061', '测试机构100061', '100060');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100062', '测试机构100062', '100061');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100063', '测试机构100063', '100061');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100064', '测试机构100064', '100060');
INSERT INTO org_info(ORG_ID, ORG_NAME, PARENT_ORG_ID) VALUES ('100065', '测试机构100065', '100064');

查询下级:

-- 创建存储函数
drop function if exists getLowOrgIds;
delimiter $$
create function getLowOrgIds(orgId varchar(20)) returns varchar(1000)
begin 
	declare temp varchar(1000); 		-- 声明所有节点机构号变量
	declare chdTemp VARCHAR(1000) ;	-- 声明临时变量下级节点机构号
	set chdTemp = orgId;
	while chdTemp is not null do
		if temp is null then set temp = chdTemp;
		else set temp = concat(temp,',',chdTemp);
		end if;
		select GROUP_CONCAT(org_id) into chdTemp from org_info where FIND_IN_SET(parent_org_id,chdTemp) >0 ;
	end while;
	return temp;
end;
$$
delimiter ;

-- 调用存储函数,并查询下级机构列表
select * from org_info where FIND_IN_SET(org_id,getLowOrgIds('100000'));

 查询上级:

drop function  if exists getparentlist;
delimiter $$
create function getparentlist(orgid varchar(20)) returns varchar(1000)   
begin   
    declare pid varchar(100);   					-- 声明单次循环上级机构号 的变量
    declare allpid varchar(1000) default orgid; -- 声明所有上级号的变量   
    while orgid is not null  do   
        set pid =(select parent_org_id as parentid from org_info where org_id = orgid);  
        if pid is not null then   
            set allpid = concat(allpid, ',', pid);         
        end if;   
	    set orgid = pid;
    end while;   
    return allpid;  
end;
$$
delimiter ;

-- 调用存储函数,并查询上级机构列表
select getparentlist('100007');
select * from org_info where find_in_set(org_id,getparentlist('100007'));

优点: 简单,方便,没有递归调用层次深度的限制 (max_sp_recursion_depth,最大255) ;

缺点:长度受限,虽然可以扩大 RETURNS varchar(1000),但总是有最大限制的。

2、sql直接查询

2.1、根据节点查询子节点:

不包含自身:

方法一:

-- 查询下级所有机构号,返回结果包含要查询的机构号'100000'
select id from (
              select t1.id,
              if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0) as ischild
              from (
                   select org_id as id, parent_org_id as parent_id from org_info t order by parent_id, id
                  ) t1,
                  (select @pids := '100000') t2 -- '100000'为要查询的节点id。  变量定义。
             ) t3 where ischild != 0;

-- 查询下级所有机构号,并拼接成字符串,返回结果不包含要查询的机构号'100000'
select GROUP_CONCAT(id) from (
              select t1.id,
              if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0) as ischild
              from (
                   select org_id as id, parent_org_id as parent_id from org_info t  order by parent_id, id
                  ) t1,
                  (select @pids := '100000') t2
             ) t3 where ischild != 0;

方法二:

		SELECT * FROM
			 (
			    SELECT * FROM org_info 
			 ) T1,
			 (SELECT @pid := '100000' ) T2
		WHERE ((FIND_IN_SET(parent_org_id,@pid) > 0 AND @pid := CONCAT(@pid, ',', org_id)));

包含自身:

-- 查询下级所有机构号,返回结果包含要查询的机构号'100004'
set @queryId = '100004'; -- 要查询的节点id
select t3.* from (
              select t1.*,
              if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), if (t1.id =@queryId,@queryId,0)) as ischild
              from (
                   select org_id as id, parent_org_id as parent_id,t.* from org_info t order by parent_id, id
                  ) t1,
                  (select @pids := @queryId) t2 -- '100004'为要查询的节点id。  变量定义。
             ) t3 where ischild != 0;

-- 查询下级所有机构号,并拼接成字符串,返回结果不包含要查询的机构号'100004'
select GROUP_CONCAT(id) from (
              select t1.id,
              if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), if (t1.id =@queryId,@queryId,0)) as ischild
              from (
                    select org_id as id, parent_org_id as parent_id,t.* from org_info t order by parent_id, id
                  ) t1,
                  (select @pids := @queryId) t2
             ) t3 where ischild != 0;

说明:

  •  @pids :='100000'是父节点赋值,此处同样可以改成普通条件查询赋值

SELECT @pids := parent.id from org_info parent where parent.org_name = '测试机构100002';
  • if (find_in_set(t1.parent_id, @pids) > 0, @pids := concat(@pids, ‘,’, t1.id), 0)

遍历表org_info,判断parent_id是否在@pids中,如果在就把当前节点加入@pids中,不在就标识为0.

  • 如果表较大,尽量增加一些条件过滤掉一些org_info表中的一些数据.

2.2、根据节点查询父节点

参看文章

-- 查询机构号
set @queryId = '100007';
-- 执行查询上级机构
SELECT d3.*,d2.lvl
    FROM ( 
        SELECT @r AS id, 
          (SELECT @r := parent_org_id FROM org_info where org_id = @r) AS tmp_parent_id,
					@l := @l + 1 AS lvl -- 查询出上级机构的顺序(100007为1,直接上级为2,以此类推)
        FROM (SELECT @r := @queryId,@l := 0  ) leafNodeId, 
             org_info hd) d2 
    INNER JOIN org_info d3 ON d2.id = d3.org_id AND d2.tmp_parent_id is not null
ORDER BY d3.org_id;


-- 或
SELECT _id as orgId,parent_org_id, lvl   FROM (
	SELECT   
		@r AS _id,   
		(SELECT @r := parent_org_id FROM org_info WHERE org_id = _id) AS parent_org_id,   
		@l := @l + 1 AS lvl   
	FROM   
		(SELECT @r := @queryId, @l := 0) vars,   
		org_info h   
		WHERE @r <> 0 and @r <> '' and @r IS NOT NULL
) tmp 

查询结果如下:

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值