无限级菜单操作(存储过程、游标、递归、事务)修改版
/**递归删除菜单**/ if exists (select * from sysobjects where name='proc_menu_P_delete') drop proc proc_menu_P_delete go create proc proc_menu_P_delete(@id int) as declare @count int,@tmpid int,@err int select @count=count(1) from tb_menu where pId=@id--通过父Id获得子集数据 if(@count>0)--判断是否存在子集数据 begin --根据父Id获得子集并将子集数据Id保存到游标中 declare ar_cursor cursor local for select id from tb_menu where pid=@id open ar_cursor--打开游标 fetch next from ar_cursor into @tmpid --取值 /**@@FETCH_STATUS是MSSQL的一个全局变量(0:FETCH 语句成功,-1:FETCH 语句失败或此行不在结果集中,-2:被提取的行不存在 @@fetch_status值的改变是通过fetch next from实现的,“FETCH NEXT FROM Cursor”)**/ while @@FETCH_STATUS=0 begin exec proc_menu_P_delete @tmpid--将游标中的子集Id作为参数调用存储过程(递归) --递归出来以后将游标中的下一个Id赋值给变量 fetch next from ar_cursor into @tmpid end close ar_cursor deallocate ar_cursor end delete tb_menu where Id=@id go /**删除菜单**/ if exists (select * from sysobjects where name='proc_menu_delete') drop proc proc_menu_delete go create proc proc_menu_delete(@id int) as begin declare @cNum int,@pId int begin try begin tran select @cNum=-(CNum+1),@pId=pId from tb_Menu where Id=@id exec proc_menu_updatecNum @pId,@cNum--修改父节点的子节点个数 exec proc_menu_P_delete @id--删除自身及其子节点 commit tran end try begin catch rollback tran end catch end go /**修改父节点的子节点数量**/ if exists (select * from sysobjects where name='proc_menu_updatecNum') drop proc proc_menu_updatecNum go create proc proc_menu_updatecNum(@pId int,@cNum int) as declare @tmpId int,@_pId int select @_pId=pId from tb_menu where id=@pId if(@_pId<>0) begin declare cor_CNum cursor local for select pId from tb_menu where id=@pId open cor_CNum fetch next from cor_CNum into @tmpId while @@FETCH_STATUS=0 begin exec proc_menu_updatecNum @tmpId,@cNum fetch next from cor_CNum into @tmpId end close cor_CNum deallocate cor_CNum end update tb_Menu set cNum=cNum+@cNum where id=@pId go /**修改子节点的深度**/ if exists (select * from sysobjects where name='proc_menu_updateLevel') drop proc proc_menu_updateLevel go create proc proc_menu_updateLevel(@id int,@level int) as begin declare @tmpid int,@count int select @count=COUNT(1) from tb_Menu where pId=@id if(@count<>0) begin declare cur_level cursor local for select id from tb_menu where pId=@id open cur_level fetch next from cur_level into @tmpid while(@@FETCH_STATUS=0) begin exec proc_menu_updateLevel @tmpid,@level fetch next from cur_level into @tmpid end close cur_level deallocate cur_level end update tb_Menu set level=level+@level where Id=@id end go /**修改节点顺序:针对Jquery的zTree控件的拖动节点排序功能**/ if exists (select * from sysobjects where name='proc_menu_updateSort') drop proc proc_menu_updateSort go create proc proc_menu_updateSort(@id int,@newpId int,@sibId int,@dir int) as begin /** @id:被移动的节点,@newpId:新父节点,@sibId:新父节点下的兄弟节点,@dir:方向,移动目标前面:1,移到后面:-1 假设:原节点父节点为1,兄弟节点值以及顺序为:5,4,3,2,1,目标父节点为2,兄弟节点值以及顺序为:3,2,1 a:现将原节点4,移动到父节点为2的子节点2的“前”面 改变后的值为: 原:5,4,3,2,1 =>(移走3并且将大于3的减1)=> 4,3,2,1 新:3,2,1 =>(将大于2的节点加1)=>4,2,1 =>(新插入的排序为:目标节点排序值+1)=>4,3(插入值),2,1 b:现将原节点4,移动到父节点为2的子节点2的“后”面 改变后的值为: 原:5,4,3,2,1 =>(移走3并且将大于3的减1)=> 4,3,2,1 新:3,2,1 =>(将大于等于2的节点加1)=>4,3,1 =>(新插入的排序为:目标节点排序值+1)=>4,3,2(插入值),1 **/ begin try begin tran --修改原兄弟节点的顺序,大于被移走的节点全部-1 declare @oldsort int,@oldpId int,@cNum int,@level int select @oldsort=sort,@oldpId=pId,@cNum=CNum,@level=level from tb_Menu where Id=@id update tb_Menu set sort=sort-1 where sort>@oldsort and pId=@oldpId if(@sibId<>0)--是否指定了兄弟节点 begin --修改新兄弟节点的顺序、修改移动后自己节点顺序 declare @newsort int,@sibIdsort int --获得目标节点的排序,并将目标节点的父节点重新付给传进来的@newpId(防止参数@newpId传错) select @sibIdsort=sort,@newpId=pId from tb_Menu where Id=@sibId if(@dir=1)--移动到目标节点的前面 begin update tb_Menu set sort=sort+1 where pId=@newpId and sort>@sibIdsort update tb_Menu set sort=@sibIdsort+1 where id=@id end else if(@dir=-1) begin update tb_Menu set sort=sort+1 where pId=@newpId and sort>=@sibIdsort update tb_Menu set sort=@sibIdsort where Id=@id end end else if(@newpId<>0 and @sibId=0)--直接移动某个父节点最下面,即没有选中目标兄弟节点 begin update tb_Menu set sort=sort+1 where pId=@newpId update tb_Menu set sort=1 where Id=@id end if(@newpId<>@oldpId)--判断是否改变了父节点 begin declare @newpLevel int set @cNum=@cNum+1 if(@newpId<>0) begin select @newpLevel=level from tb_Menu where id=@newpId end else begin set @newpLevel=0 end update tb_Menu set pId=@newpId where Id=@id declare @levelCount int PRINT @newpLevel set @levelCount=@newpLevel+1-@level exec proc_menu_updateLevel @id,@levelCount exec proc_menu_updatecNum @newpId,@cNum set @cNum=-(@cNum) exec proc_menu_updatecNum @oldpId,@cNum end commit tran end try begin catch rollback tran end catch end go /**新增节点**/ if exists (select * from sysobjects where name='proc_menu_add') drop proc proc_menu_add go create proc proc_menu_add(@name varchar(50),@pId int,@id int output) as begin begin try begin tran declare @pLevel int if(@pId<>0) begin select @pLevel=level+1 from tb_Menu where Id=@pId end else begin set @pLevel=1 end insert into tb_Menu(name,twoname,pId,level,cNum,state,sort,dt,url,des) values(@name,'',@pId,@pLevel,0,1,0,getdate(),'','') set @id=@@IDENTITY select @id update tb_Menu set sort=sort+1 where pId=@pId--兄弟元素排序加1 exec proc_menu_updatecNum @pId,1--修改父节点的子节点数量 commit tran end try begin catch set @id=0 rollback tran end catch end go /**根据节点获得所有子节点数据**/ --方法1:连接查询 if exists (select * from sysobjects where name='proc_menu_select') drop procedure proc_menu_select go create proc proc_menu_select(@id int) as declare @i int declare @count int, @tmpid int create table #tb( id int, name varchar(50), twoname varchar(50), pId int, level int, state int, sort int, url varchar(100) ) select @count=COUNT(1) from tb_Menu where pId=@id select @i=level-1 from tb_Menu where pId=@id insert #tb select m.id,m.name,m.twoname,m.pId,m.level,m.state,m.sort,m.url from tb_Menu m where pId=@id while(@count<>0) begin set @i=@i+1 select @count=COUNT(1) from tb_Menu m left join #tb on #tb.id=m.pId where #tb.level=@i insert #tb select m.id,m.name,m.twoname,m.pId,m.level,m.state,m.sort,m.url from tb_Menu m left join #tb on #tb.id=m.pId where #tb.level=@i end select id,name,twoname,pId,level,state,sort,url from #tb order by sort desc go --方法2:递归查询 if exists(select * from sysobjects where name='proc_menu_sele') drop proc proc_menu_sele go create proc proc_menu_sele(@id int,@t int) as if(@t<>0) begin declare @tmppId int select @tmppId=pId from tb_Menu where Id=@id create table #tab( id int, name varchar(50), twoname varchar(50), pId int, level int, state int, sort int, url varchar(100) ) end declare @tmpid int,@count int select @count=COUNT(1) from tb_Menu where Id=@id if(@count<>0) begin insert INTO #tab select id,name,twoname,pId,level,state,sort,url from tb_Menu where Id=@id declare cur_select cursor local for select id from tb_menu where pId=@id open cur_select fetch next from cur_select into @tmpid while(@@FETCH_STATUS=0) begin exec proc_menu_sele @tmpid,0 fetch next from cur_select into @tmpid end close cur_select deallocate cur_select end else begin insert INTO #tab select id,name,twoname,pId,level,state,sort,url from tb_Menu where Id=@id end declare @p int select @p=pId from tb_Menu where Id=@id if(@p=@tmppId) begin select * from #tab order by sort desc end go