背景
公司的一個業務系統中有區域表,整個區域是一個樹結構,為了方便根據某一父節點查詢所有葉子節點,提供了一個額外的字段path,按照分隔符存儲了從根節點到當前節點的總路徑。
表結構如下:
create table t_area
(
area_id varchar(32) not null comment '主鍵' primary key,
area_pid varchar(32) null comment '父級區域ID',
area_name varchar(30) null comment '區域名稱',
level varchar(2) null comment '層級 從1開始',
external_name varchar(30) null comment '對外名稱',
flag varchar(3) null comment '特殊標記 0=沒有標記 1=特殊入門',
path varchar(200) null
)
comment '區域位置表';
比如,有以下路徑:
- A樓-13層-1301室
- A樓-13層-1302室
- A樓-13層-1303室
那么,
- 在1301的path字段存儲為:A樓id#13層id#1301id
- 在1302的path字段存儲為:A樓id#13層id#1302id
- 在1303的path字段存儲為:A樓id#13層id#1303id
目前的需求是,需要查詢給定葉子節點的完整路徑名稱,但是待查詢的表中只存儲了葉子節點的area_id
有兩種方式可以實現:
- 寫一個SQL查詢出葉子節點id,對應的path,在Java層面,按照#切割字符串,得到每一層的area_id,再用SQL查詢出area_id對應的area_name,拼接好返回
- 在MySQL中實現一個自定義函數,傳入葉子節點的id,即可獲取完整路徑名稱。
如果使用第二種方式,該業務SQL就變得很簡單,考慮用函數實現。
函數實現
CREATE DEFINER=`sgsv2`@`%` FUNCTION `getFullArea`(`areaId` varchar(50)) RETURNS varchar(50) CHARSET utf8
BEGIN
set @area_id=areaId;
-- select @area_id;
select path from t_area where area_id = @area_id limit 1 into @path;
-- select @path;
SET @i=2;
SET @count=(LENGTH(@path)-LENGTH(REPLACE(@path ,'#',''))) + 1;
set @returnStr="";
-- select @count;
WHILE @i <= @count
DO
set @areaTmpId=(SUBSTRING_INDEX(SUBSTRING_INDEX(@path,'#',@i),'#',-1));
-- select @areaTmpId;
select area_name from t_area where area_id = @areaTmpId into @areaTmpName;
-- select @areaTmpName;
if @i > 2 Then
set @returnStr=concat(@returnStr,'-',@areaTmpName);
else
set @returnStr=concat(@returnStr,@areaTmpName);
end if;
-- set @returnStr=concat(@returnStr,'-',@areaTmpName);
-- select @returnStr;
SET @i=@i+1;
END WHILE;
-- select @returnStr;
RETURN @returnStr;
END
要點:獲取path后,如何遍歷獲取每層的area_id?
我這里的實現是,先用下面的語句,獲取總area_id的個數
SET @count=(LENGTH(@path)-LENGTH(REPLACE(@path ,'#',''))) + 1;
根據獲得的count數進行遍歷,在遍歷中獲取每一個area_id,關鍵語句如下:
set @areaTmpId=(SUBSTRING_INDEX(SUBSTRING_INDEX(@path,'#',@i),'#',-1));
最后使用concat函數拼接得到最終的結果
set @returnStr=concat(@returnStr,'-',@areaTmpName);
技巧:可以使用存儲過程來調試
在函數中, 無法像存儲過程一樣,使用select @變量名來調試,所以我先把函數主題復制到一個存儲過程中,這樣就可以調試了!