Mysql实现Oracle中的Start with...Connect by方式
作者:HNT_Wings
文章总结:作者在迁移数据库时,使用了Oracle的startwith进行树的递归查询,但遇到了一些问题,通过搜索和修改,作者成功地使用存储过程和预处理语句来实现动态查询,并解决了变量声明和预处理语句的问题
Mysql Oracle中的Start with...Connect by
工作需要,迁移数据库时发现使用了Oracle中的start with来进行树的递归查询,所以自己动手丰衣足食。
通过一番搜索后发现
大家的实现基本都是这样的:
CREATE FUNCTION queryChildrenAreaInfo(areaId INT) RETURNS VARCHAR(4000) BEGIN DECLARE sTemp VARCHAR(4000); DECLARE sTempChd VARCHAR(4000); SET sTemp='$'; SET sTempChd = CAST(areaId AS CHAR); WHILE sTempChd IS NOT NULL DO SET sTemp= CONCAT(sTemp,',',sTempChd); SELECT GROUP_CONCAT(id) INTO sTempChd FROM t_areainfo WHERE FIND_IN_SET(parentId,sTempChd)>0; END WHILE; RETURN sTemp;
但是这样的代码没法复用(而且我的navicat居然建立函数失败,或者各种错误,所以我使用了存储过程,效果一样),所以我们使用set和execute来进行语句的拼接和执行,
修改后
如下:
CREATE PROCEDURE getChildList
IN rootId DECIMAL(65),
IN tablesname VARCHAR(6000),
OUT sTemp VARCHAR(6000)
BEGIN
DECLARE sTempChd VARCHAR(4000);
SET sTemp='$';
SET sTempChd = CAST(rootId AS CHAR);
WHILE sTempChd IS NOT NULL DO
SET sTemp= CONCAT(sTemp,',',sTempChd);
set @sqlexe = concat("SELECT GROUP_CONCAT(id) INTO sTempChd FROM " , tablesname , " WHERE FIND_IN_SET(parentId,sTempChd)>0;")
prepare sqlexe from @sqlexe;
execute sqlexe;
END WHILE;
END;这样一来我们就可以将表名作为参数传入,但是一番执行后,你会发现,哦豁,它居然报了这样一个错:
1327 - Undeclared variable: sTempChd;
这个低级错误困扰了我半天,我不是声明了sTempChd为declare吗?
答案很简单
预处理语句(也就是我们的prepare)中,只接受@声明的参数。因为在存储过程中,使用动态语句,预处理时,动态内容必须赋给一个会话变量,也就是@形式声明的变量,而declare声明的是存储过程变量,具体的内容涉及到更深的知识,我暂时无法找到原因。
前文是自上而下的查询,自下而上的查询其实很简单,只要替换一下参数和语句内容就可以了,
具体如下:
CREATE PROCEDURE `getParentList`(
IN rootId DECIMAL(65,0),
IN tablesname VARCHAR ( 500 ),
OUT sTemp VARCHAR ( 6000 ) )
BEGIN
DECLARE
PARENTID DECIMAL(65);
SET sTemp = '$';
SET @sTempChd = cast(rootId as char);
WHILE @sTempChd <> 0 DO
SET sTemp = concat( sTemp, ',', @sTempChd );
SET @sqlcmd = CONCAT("SELECT PARENTID into @sTempChd FROM " , tablesname , " WHERE CATEID = " , @sTempChd , ";");
PREPARE stmt FROM @sqlcmd;
EXECUTE stmt;
END WHILE;
DEALLOCATE PREPARE stmt;
END别忘了,最后要执行一下deallocate语句,释放预处理sql,免得session的预处理语句过多,达到max_prepared_stmt_count的上限值。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
