无限级分类存储过程版

/*
无限级分类存储过程版

(未作详细测试)
自己写的无限级分类存储过程版,参照网上那个的无限级分类存储过程版的实现原理,主要修改了: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

 

原文地址:https://www.cnblogs.com/0000/p/1600951.html