/*
无限级分类存储过程版
(未作详细测试)
自己写的无限级分类存储过程版,参照网上那个的无限级分类存储过程版的实现原理,主要修改了:1、为表的
排序字段添加聚集索引,速度更快;2、修改更新节点的存储过程的程序,相对比较难实现;3、其它略作修改
*/
--数据表名:Category
--数据字段:
/*
[CategoryId] [int] NOT NULL (主键,非聚集)
[ParentId] [int] NOT NULL
[Name] [nvarchar](50)
[Depth] [int] NOT NULL
[OrderId] [int] NOT NULL(聚集索引)
[Path] [nvarchar](1000)
[Intro] [nvarchar](1000)
*/
------------------------------------------------------------------
--1、创建表:
--对象: Table [dbo].[Category]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Category]
(
[CategoryId] [int] NOT NULL,
[ParentId] [int] NOT NULL,
[Name] [nvarchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL,
[Depth] [int] NOT NULL, --深度
[OrderId] [int] NOT NULL, --排序
[Path] [nvarchar](1000) COLLATE Chinese_PRC_CI_AS NOT NULL, --路径
[Intro] [nvarchar](1000) COLLATE Chinese_PRC_CI_AS NULL, --简介
--CONSTRAINT [PK_Category] PRIMARY KEY NONCLUSTERED ([CategoryId] ASC)WITH (IGNORE_DUP_KEY =
OFF) ON [PRIMARY]
-- 定义主键并去除默认的 聚焦索引 2k 不可用
)
ON [PRIMARY]
--2、创建聚集索引
-- 对象: Index [IX_Category_OrderId]
CREATE CLUSTERED INDEX [IX_Category_OrderId] ON [dbo].[Category]
(
[OrderId] ASC
)
-- 2k 不可用
--WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON
[PRIMARY]
--3、创建添加、修改、删除、查询 四个存储过程
--添加:
--对象: StoredProcedure [dbo].[Category_Insert]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- exec Category_Insert 0,"a","desc"
-- exec Category_Insert 0,"b","desc"
-- exec Category_Insert 0,"c","desc"
-- exec Category_Insert 1,"a_1","desc"
-- exec Category_Insert 1,"a_2","desc"
-- exec Category_Insert 1,"a_3","desc"
-- exec Category_Insert 2,"b_1","desc"
-- exec Category_Insert 2,"b_2","desc"
-- exec Category_Insert 2,"b_3","desc"
-- exec Category_Insert 3,"c_1","desc"
-- exec Category_Insert 3,"c_2","desc"
-- exec Category_Insert 3,"c_3","desc"
-- exec Category_Insert 5,"a_2_1","desc"
-- exec Category_Insert 5,"a_2_2","desc"
-- exec Category_Insert 5,"a_2_3","desc"
CREATE PROCEDURE [dbo].[Category_Insert]
@ParentId int,
@Name nvarchar(50),
@Intro nvarchar(1000)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ERR int
SET @ERR=0
BEGIN TRAN
IF @ParentId<0 OR ISNULL(@Name,'')=''
BEGIN
SET @ERR=1
GOTO theEnd
END
DECLARE @CategoryId int,@Depth int,@OrderId int,@Path nvarchar(1000)
--计算@CategoryId
SELECT @CategoryId=ISNULL(MAX(CategoryId)+1,1) FROM Category
IF @ParentId=0
BEGIN
--计算@Depth
SET @Depth=1
--计算@OrderId
SELECT @OrderId=ISNULL(MAX(OrderId)+1,1) FROM Category
--计算@Path
SET @Path=LTRIM(@CategoryId)
END
ELSE
BEGIN
--计算@Depth,以及@Path的前半部分,即父节点的Path,计算@OrderId时需要用到
SELECT @Depth=Depth+1,@Path=Path FROM Category WHERE CategoryId=@ParentId
--计算@OrderId
SELECT @OrderId=MAX(OrderId)+1 FROM Category
WHERE CategoryId=@ParentId OR Path LIKE @Path+'|%'
--向后移动插入位置后面的所有节点
UPDATE Category SET OrderId=OrderId+1 WHERE OrderId>=@OrderId
--计算@Path
SET @Path=@Path+'|'+LTRIM(@CategoryId)
END
INSERT INTO Category(CategoryId,ParentId,Name,Depth,OrderId,Path,Intro) VALUES
(@CategoryId,@ParentId,@Name,@Depth,@OrderId,@Path,@Intro)
IF @@ERROR<>0
SET @ERR=-1
theEnd:
IF @ERR=0
BEGIN
COMMIT TRAN
RETURN 0
END
ELSE
BEGIN
ROLLBACK TRAN
RETURN @ERR
END
END
--修改:
--对象: StoredProcedure [dbo].[Category_Update] 脚本日期: 12/11/2007 作者:Figo Chen
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- exec Category_Update 4,12,'c_3--',''
CREATE PROCEDURE [dbo].[Category_Update]
@CategoryId int,
@ParentId int,
@Name nvarchar(50),
@Intro nvarchar(1000)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ERR int
SET @ERR=0
BEGIN TRAN
--如果参数不正确,取消操作
IF(@CategoryId<=0 OR @ParentId<0 OR @CategoryId=@ParentId OR ISNULL(@Name,'')='')
BEGIN
SET @ERR=1
GOTO theEnd
END
--如果不存在此节点,则取消操作
IF(NOT EXISTS(SELECT CategoryId FROM Category WHERE CategoryId=@CategoryId))
BEGIN
SET @ERR=2
GOTO theEnd
END
--如果不存在父节点,则取消操作
IF(NOT EXISTS(SELECT CategoryId FROM Category WHERE CategoryId=@ParentId) AND @ParentId<>0)
BEGIN
SET @ERR=3
GOTO theEnd
END
--先获取原来的值
DECLARE @oldParentId int,@oldDepth int,@oldOrderId int,@oldPath nvarchar(1000)
SELECT @oldParentId=ParentId,@oldDepth=Depth,@oldOrderId=OrderId,@oldPath=Path
FROM Category WHERE CategoryId=@CategoryId
--如果新的父节点是当前节点的子(孙)节点,则取消操作
IF(EXISTS(SELECT CategoryId FROM Category WHERE CategoryId=@ParentId AND Path LIKE
@oldPath+'|%'))
BEGIN
SET @ERR=4
GOTO theEnd
END
--根据是否需要修改父节点,分两种情况更新
IF @ParentId=@oldParentId
BEGIN
UPDATE Category SET Name=@Name,Intro=@Intro WHERE CategoryId=@CategoryId
END
ELSE
BEGIN
--获取要移动的节点个数[当前节点加上当前节点的子(孙)节点]
DECLARE @MoveCount int
SELECT @MoveCount=COUNT(CategoryId) FROM Category WHERE Path LIKE @oldPath+'%'
--获取新父节点的信息,根据新父节点的信息获得当前节点的新值
DECLARE @Parent_Depth int,@Parent_oldOrderId int,@Parent_newOrderId int,@Parent_Path
nvarchar(1000)
DECLARE @newDepth int,@newOrderId int,@newPath nvarchar(1000)
----根据父节点是否根节点,分两种情况
IF @ParentId=0
BEGIN
SET @Parent_Depth=0
SET @Parent_Path=''
SELECT @Parent_oldOrderId=MAX(OrderId)+1 FROM Category WHERE ParentId=@ParentId
SET @newDepth=1
SET @newPath=LTRIM(@CategoryId)
END
ELSE
BEGIN
SELECT @Parent_Depth=Depth,@Parent_Path=Path,@Parent_oldOrderId=OrderId FROM
Category WHERE CategoryId=@ParentId
SET @newDepth=@Parent_Depth+1
SET @newPath=@Parent_Path+'|'+LTRIM(@CategoryId)
END
SET @newOrderId=@oldOrderId --初始化,因为有可能OrderId最终没有变化
--移动相关节点,并获取当前节点的新序号值
DECLARE @temp_newOrderId int,@StartOrderId int,@EndOrderId int,@Offset int
--获得移动前当前节点目标位置原来的序号
SELECT @temp_newOrderId=MAX(OrderId)+1 FROM Category
WHERE Path LIKE @Parent_Path+'%' AND Path NOT LIKE @oldPath+'%'
IF @temp_newOrderId<@oldOrderId --向前移动
BEGIN
SELECT @StartOrderId=MAX(OrderId)+1 FROM Category WHERE Path LIKE
@Parent_Path+'%'
SET @EndOrderId=@oldOrderId-1
SET @Offset=@MoveCount
SET @newOrderId=@StartOrderId --获取当前节点新序号值方法之一(1)
END
ELSE IF @temp_newOrderId>@oldOrderId --向后移动
BEGIN
SET @StartOrderId=@oldOrderId+@MoveCount
SELECT @EndOrderId=MAX(OrderId) FROM Category WHERE Path LIKE
@Parent_Path+'%'
SET @Offset=-@MoveCount
SET @newOrderId=@EndOrderId-@MoveCount+1 --获取当前节点新序号值方法之一(2)
END
IF @newOrderId<>@oldOrderId
UPDATE Category SET OrderId=OrderId+@Offset
WHERE OrderId BETWEEN @StartOrderId AND @EndOrderId AND Path NOT LIKE
@oldPath+'%'
--获取当前节点新序号值方法之二(方法一效率高,方法二可读性强)
--SELECT @newOrderId=MAX(OrderId)+1 FROM Category WHERE Path LIKE @Parent_Path+'%'
AND @EndOrderId AND Path NOT LIKE @oldPath+'%'
--更新当前节点
UPDATE Category SET
ParentId=@ParentId,Name=@Name,Depth=@newDepth,OrderId=@newOrderId,Path=@newPath,Intro=@Intro
WHERE CategoryId=@CategoryId
--更新子节点
UPDATE Category SET Depth=Depth-@oldDepth+@newDepth,OrderId=OrderId-
@oldOrderId+@newOrderId,Path=REPLACE(Path,@oldPath,@newPath)
WHERE Path LIKE @oldPath+'|%'
END
IF @@ERROR<>0
SET @ERR=-1
theEnd:
IF @ERR=0
BEGIN
COMMIT TRAN
RETURN 0
END
ELSE
BEGIN
ROLLBACK TRAN
RETURN @ERR
END
END
--删除:
--对象: StoredProcedure [dbo].[Category_Delete]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[Category_Delete]
@CategoryId int
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ERR int
SET @ERR=0
BEGIN TRAN
--如果参数不正确,则取消操作
IF(@CategoryId<=0)
BEGIN
SET @ERR=1
GOTO theEnd
END
--如果不存在此节点,则取消操作
IF(NOT EXISTS(SELECT CategoryId FROM Category WHERE CategoryId=@CategoryId))
BEGIN
SET @ERR=2
GOTO theEnd
END
--如果存在子节点,则不删除
IF(EXISTS(SELECT CategoryId FROM Category WHERE ParentId=@CategoryId))
BEGIN
SET @ERR=3
GOTO theEnd
END
DECLARE @OrderId int
--先提取节点的序号值,供以后使用
SELECT @OrderId=OrderId FROM Category WHERE CategoryId=@CategoryId
--删除节点
DELETE FROM Category WHERE CategoryId=@CategoryId
--向前移动后面的所有节点的位置
UPDATE Category SET OrderId=OrderId-1 FROM Category WHERE OrderId>@OrderId
IF @@ERROR<>0
SET @ERR=-1
theEnd:
IF @ERR=0
BEGIN
COMMIT TRAN
RETURN 0
END
ELSE
BEGIN
ROLLBACK TRAN
RETURN @ERR
END
END
--选择:
--对象: StoredProcedure [dbo].[Category_Select]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[Category_Select]
AS
BEGIN
SET NOCOUNT ON;
SELECT CategoryId,ParentId,Name,Depth,Intro FROM Category ORDER BY OrderId ASC
END