mysql 存储过程 无限分类,查看新闻/公告--[转帖]mysql存储过程实现的无限级分类,前序遍历树...
原理說明可以看這個帖子:
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
總結
以上是生活随笔為你收集整理的mysql 存储过程 无限分类,查看新闻/公告--[转帖]mysql存储过程实现的无限级分类,前序遍历树...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么键盘好(机械键盘十大名牌)
- 下一篇: mysql复杂操作,.Net Core+