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的上限值。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。