在大部分的大型程序里都会用到树,种树这个鸟活让我头大了几天。
树的算法大家应该都很清楚了,递归就可以了,但是头疼的就是树的排序,在添加、修改父节点,节点移动都要重新计算树的排序,从而是代码量大大提高。于是决定用SQL去做这个烦琐的事情,用了2个触发器,一个存储过程完成了树的排序。
![](/Images/OutliningIndicators/ContractedBlock.gif)
Code
protected System.Web.UI.WebControls.Label Label1;
protected Microsoft.Web.UI.WebControls.TreeView ModuleTree;
DataSet gObjDs = new DataSet();
private void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
isShowSystemMenu = false;
isShowToolBar = false;
if(!Page.IsPostBack)
{
//ModuleTree.Nodes.Clear();
CommonLib.DataCommon mObjData = new DataCommon("default");
string mStrSql = "Select * From ModuleBase";
gObjDs = mObjData.getDSBySQL(mStrSql);
BindTree(ModuleTree.Nodes[0].Nodes,0);
ModuleTree.Nodes[0].Expanded=true;
mObjData.CommDispone();
}
}
public void BindTree(TreeNodeCollection mObjNds , int mIntParentID)
{
DataView mObjDv = new DataView();
TreeNode mObjTmpNd;
int mIntModID;
mObjDv.Table = gObjDs.Tables[0];
mObjDv.RowFilter = "ParentID=’" + mIntParentID + "’";
mObjDv.Sort = "SQNC";
foreach(DataRowView mObjRow in mObjDv)
{
mObjTmpNd = new TreeNode();
mIntModID=(int)mObjRow["ModID"];
mObjTmpNd.ID=mIntModID.ToString();
mObjTmpNd.Text=mObjRow["ModName"].ToString();
//mObjTmpNd.ExpandedImageUrl =mObjRow["ImgEx"].ToString();
//mObjTmpNd.ImageUrl=mObjRow["Img"].ToString() ;
//mObjTmpNd.NavigateUrl =mObjRow["Adderss"].ToString() ;
//mObjTmpNd.target="右面框架的名字";
if((int)mObjRow["IsEntrance"]==0)
{
mObjTmpNd.Type="Catalog";
}
else
{
mObjTmpNd.Type="Node";
}
mObjNds.Add(mObjTmpNd);
BindTree(mObjTmpNd.Nodes,mIntModID);
}
}
树的递归排序
![](/Images/OutliningIndicators/ContractedBlock.gif)
Code
create trigger Inster_ModuleBase on [ModuleBase]
for insert
AS
begin
declare @SQNC int,@ParentID int
select @ParentID=ParentID from inserted
select @SQNC = isNull(Max(SQNC),0) from ModuleBase where ParentID=@ParentID
update ModuleBase Set SQNC=@SQNC+1 where ModID=(select ModID from inserted)
end
go
第一个触发器实现添加节点的时候,对排序进行计算根据插入的父节点的ID,来判断该节点下最大的排序值是多少,如果该节点下没有子节点,返回0,那么排序也就是0+1,排序在第一位,如果有节点,根据该父节点下子节点最大的排序值+1,来确定新添加的记录的排序值
![](/Images/OutliningIndicators/ContractedBlock.gif)
Code
Create trigger update_ModuleBase on [ModuleBase]
for update
as
begin
if update(ParentID)
begin
declare @SQNC int,@ParentID int
select @ParentID=ParentID from inserted
select @SQNC = isNull(Max(SQNC),0) from ModuleBase where ParentID=@ParentID
update ModuleBase Set SQNC=@SQNC+1 where ModID=(select ModID from inserted)
end
end
go
第二个触发器在更改父节点的ID的时候触发,比如,我把根节点的子节点1下的子节点1-1
移动只至根节点下的子节点2,那么这时候排序值仍然需要重新计算,计算方法同上个触发器,移动过去后,该节点会位于新的父节点的尾部。
![](/Images/OutliningIndicators/ContractedBlock.gif)
Code
Create PROCEDURE ModuleBase_move
@ModID int,@Menth nvarchar(5),@result nvarchar(30) output
As
declare @ParentID int,@JHSQNC int,@TmpModID int
select @ParentID = ParentID from ModuleBase where ModID = ModID
DECLARE ModuleBase_Cursor CURSOR SCROLL FOR
SELECT ModID FROM ModuleBase WHERE ParentID=@ParentID ORDER BY SQNC
OPEN ModuleBase_Cursor
FETCH NEXT FROM ModuleBase_Cursor INTO @TmpModID
WHILE @@FETCH_STATUS = 0
BEGIN
if(@TmpModID=@ModID)
begin
if(@Menth=’up’)
begin
FETCH PRIOR FROM ModuleBase_Cursor INTO @TmpModID
end
else
begin
FETCH NEXT FROM ModuleBase_Cursor INTO @TmpModID
end
select @JHSQNC=SQNC FROM ModuleBase where ModID=@TmpModID
update ModuleBase Set SQNC=(select SQNC from ModuleBase where ModID=@ModID) where ModID=@TmpModID
update ModuleBase Set SQNC=@JHSQNC where ModID=@ModID
FETCH LAST FROM ModuleBase_Cursor into @TmpModID
End
FETCH NEXT FROM ModuleBase_Cursor into @TmpModID
END
CLOSE ModuleBase_Cursor
DEALLOCATE ModuleBase_Cursor
IF @@ERROR <> 0
BEGIN
set @result=’操作失败’
END
ELSE
BEGIN
set @result=’操作成功’
END
GO
存储过程是为了在子节点在父节点下上下移动时候使用,
传入值两个@ModID int------节点编号
@Menth nvarchar(5)-------移动方式up或者down
返回值@result,用来输出是否移动成功
实现原理是将移动节点的父节点按顺序排列,如果上移动,找到该移动节点,与上条记录的排序值互换,下移则相反!
这样操作起来在程序的代码中可以不必考虑节点排序的问题,一切让SQL自己做