[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[转帖]mysql存储过程实现的无限级分类,前序遍历树

上一篇:[备忘]挺好用的一个javascript的日期控件
下一篇:[转帖]MySQL存储过程中使用SELECT …INTO语句为变量赋值

添加日期:2013/1/30 14:51:04 快速返回   返回列表 阅读4043次
原理说明可以看这个帖子:
http://jakezzz.blog.sohu.com/53099673.html

把代码整理一下,试了一下,比较好使,根据实际需要改改就基本OK。

(1)建表


DROP TABLE IF EXISTS `pcms_channel`;

CREATE TABLE IF NOT EXISTS `pcms_channel` (
  `cid` tinyint(3) unsigned NOT NULL auto_increment,
  `name` char(10) NOT NULL COMMENT '频道名称',
  `parentid` tinyint(4) NOT NULL COMMENT '父级ID',
  `lft` tinyint(4) NOT NULL COMMENT '左值',
  `rgt` tinyint(4) NOT NULL COMMENT '右值',
  `lv` tinyint(3) unsigned NOT NULL default '0' COMMENT '级层',
  `themeid` tinyint(3) unsigned NOT NULL default '1' COMMENT '使用的主题的ID',
  PRIMARY KEY  (`cid`),
  KEY `parentid` (`parentid`,`lft`,`rgt`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;



(2)导入测试数据


INSERT INTO `pcms_channel` (`cid`, `name`, `parentid`, `lft`, `rgt`, `lv`, `themeid`) VALUES
(1, 'phpoocms', 0, 1, 12, 0, 1),
(2, 'test', 1, 2, 7, 1, 1),
(3, 'te', 2, 3, 6, 2, 1),
(4, 'tes', 1, 8, 9, 1, 1),
(5, 'dd', 3, 4, 5, 3, 1),
(6, 'fromphp', 1, 10, 11, 1, 1);



(3)添加节点的存储过程
调用方法:call addChannel (1,"aaaa")
返回1000才是成功,其它是失败了。


DROP Procedure `addChannel` ;

DELIMITER |

/*
* pid,添加到的目标节点ID
* name,节点名称
*/
CREATE PROCEDURE `addChannel`(in pid int,in name varchar(20))
BEGIN
    DECLARE pr INT;/*右值*/
    DECLARE lvv INT;/*层级*/
    DECLARE aff INT; /* SQL 影响记录条数 */
    DECLARE af INT DEFAULT 0; /* 总影响记录条数*/
    
    SET @result = null;
    
    /* 目标节点的右值,Level值 */
    SELECT `rgt`,`lv` INTO pr,lvv FROM `pcms_channel` WHERE `cid` = pid;
    
    IF pr THEN
        START TRANSACTION;
        
        /* 更新右侧节点的left值 */
        UPDATE `pcms_channel` SET `lft`=`lft`+2 WHERE `lft`>pr;
        SELECT ROW_COUNT() INTO aff;
        SET af = aff+af;
        
        /* 更新右侧节点的right值 */
        UPDATE `pcms_channel` SET `rgt`=`rgt`+2 WHERE `rgt`>=pr;
        SELECT ROW_COUNT() INTO aff;
        SET af = aff+af;
        
        /* 增加节点自己 */
        INSERT INTO `pcms_channel` (`name`,`parentid`,`lft`,`rgt`,`lv`) VALUES (name,pid,pr,pr+1,lvv+1);
        SELECT ROW_COUNT() INTO aff;
        SET af = aff+af;
        
        IF af >= 2 THEN
            COMMIT;
            SET @result = 1000;
            SELECT 1000 AS result;
        ELSE
            ROLLBACK;
            SET @result = 1002;
            SELECT 1002 AS result;
        END IF;
    ELSE
        SET @result = 1001;
        SELECT 1001 AS result;
    END IF;
END |



(4)删除节点(只能删除叶节点,有子节点的不允许删除)
调用方法:call delChannel (5)


DROP Procedure `delChannel` ;

DELIMITER |

/*
* pid,要删除的节点ID
* 节点有子节点时,不允许删除,
* 所以问题简单了,只删除自己就可以了,不需要删除所有子节点。
*/
CREATE PROCEDURE `delChannel`(in pid int)
BEGIN

    DECLARE pl INT; /* 左节点ID */
    DECLARE pn INT; /* 子节点数量 */
    DECLARE aff INT; /* SQL 影响记录条数 */
    DECLARE af INT DEFAULT 0; /* 总影响记录条数*/

    SET @result = null;
    SET @parentid = null;
    SET @name = null;

    /* 查询要删除的节点,及它的子节点 ,得到左节点ID,子节点数量,父节点ID和节点名称是给移动节点使用的。 */
    SELECT a.`lft`,IFNULL(COUNT(b.`cid`),0),a.`parentid`,a.`name` 
    INTO pl,pn,@parentid,@name 
    FROM `pcms_channel` AS a LEFT JOIN `pcms_channel` AS b 
        ON a.`cid`=b.`parentid` 
    WHERE a.`cid`=pid
    GROUP BY b.`parentid`;

    /* 如果子节点数量为0 */
    IF pl&&!pn THEN
        IF pl!=1 THEN /* 左节点是1的认为是根节点,不让删除*/
            START TRANSACTION;

            /* 更新右侧节点的left值 */
            UPDATE `pcms_channel` SET `lft`=`lft`-2 WHERE `lft`>pl;
            SELECT ROW_COUNT() INTO aff;
            SET af = aff+af;

            /* 更新右侧节点的right值 */
            UPDATE `pcms_channel` SET `rgt`=`rgt`-2 WHERE `rgt`>pl;
            SELECT ROW_COUNT() INTO aff;
            SET af = aff+af;
            
            /* 删除节点自己 */
            DELETE FROM `pcms_channel` WHERE `cid` = pid;
            SELECT ROW_COUNT() INTO aff;
            SET af = aff+af;

            IF af >= 2 THEN
                COMMIT;
                SET @result = 1000;
                SELECT 1000 AS result;
            ELSE
                ROLLBACK;
                SET @result = 1002;
                SELECT 1002 AS result;
            END IF;
        ELSE
            SET @result = 1004;
            SELECT 1004 AS result;
        END IF;
    ELSEIF pn&&pl THEN /* 子节点数量>0则报错 */
        SET @result = 1003;
        SELECT 1003 AS result;
    ELSE
        SET @result = 1001;
        SELECT 1001 AS result;
    END IF;
END |




(5)移动节点(只能移动叶节点,是通过先删除,再添加的办法实现的。)
调用方法:call moveChannel (5,4)


DROP Procedure `moveChannel` ;

DELIMITER |

/*
* pid,移动的节点ID
* tid,目标节点ID
*/
CREATE PROCEDURE `moveChannel`(pid int,tid int)
BEGIN
    /* 节点ID是为1的是根节点,不让移动(这么判断不对吧?左节点是1的不让删除吧) */
    IF pid=1 THEN
        SELECT 1004 AS result;
    ELSE
        /* 不能移动到自己 */
        IF pid!=tid THEN
            call delChannel (pid); /* 先删除该节点 */
            IF @result=1000 THEN
                call addChannel (tid,@name); /* 再添加到目标节点下 */
                IF @result THEN
                    SELECT 1000 AS result;
                ELSE
                    call addChannel (@parentid,@name); /* 添加失败的话,再添加到原来的位置*/
                    SELECT @result AS result;
                END IF;
            ELSE
                SELECT @result AS result;
            END IF;
        ELSE
            SELECT 1005 AS result;
        END IF;
    END IF;
    SET @result=null;
    SET @parentid=null;
    SET @name=null;
END |



------------------------用法--------------------------
(0)得到整个树形结构:
select cid,concat(repeat("-",lv),name) from pcms_channel order by lft;

(1)获取节点的所有子孙节点:
---------------------------
如节点2的lft是2,rgt是7

select lft,rgt from pcms_channel where cid = 2;

select cid,concat(repeat("-",lv),name) from pcms_channel 
where lft between 2 and 7
order by lft;
----------------------------

(2)获取节点所在路径:
---------------------------
如节点5的lft是4,rgt是5

select lft,rgt from pcms_channel where cid = 2;

select cid,concat(repeat("-",lv),name) from pcms_channel 
where lft<4 and rgt>5
order by lft;

会得到从根节点到该节点的所有节点,拼接即可
---------------------------

(3)获取所有子孙节点的个数
个数= (right – left - 1) / 2
 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved