多かれ少なかれ、ツリー ディレクトリや製品分類などの 2 レベルまたは 3 レベルのメニューを作成しましたが、さらに多くのレベルの分類が必要な場合は、通常、再帰を使用します。プログラムで再帰を使用すると、パフォーマンスのオーバーヘッドが多かれ少なかれ増加します。
以前はASP.netを使って非再帰的な無限レベルの分類ディレクトリをプログラム内に実装していましたが、移植性が弱いことを考慮してストアドプロシージャに変更してみんなで勉強できるように公開することにしました。テストプロセス中に問題は見つかりませんでした。また、コードは最適化されていませんでした。
通常、ほとんどの操作はディレクトリを読み取ることなので、次の実装では、再帰を使用しない場合、読み取る必要があるのは 1 つの Select ステートメントのみであり、理論的にはレベルは無限です。
================================================= ====================
テーブル構造:
テーブル名: Tb_Column
テーブル構造 (すべてのフィールドは空ではありません):
Column_ID int 主キー (注: 非識別子)
Column_Name nvarchar(50) 分類名
Parent_ID int 親カテゴリ ID (デフォルト値 0)
Column_Path nvarchar(1000) 分類パス
Column_Depth int 分類の深さ (デフォルト値 0)
Column_Order int ソート (デフォルトは 0)
Column_Intro nvarchar(1000) 分類の説明
=========================================== === ==================
ストアド プロシージャ 1: 新しい分類の作成
CREATE PROCEDURE sp_Column_Insert
(
@Parent_ID int,
@Column_Name nvarchar(50),
@Column_Intro nvarchar(1000)
)
として
@Err As int を宣言する
@Err=0 を設定して
トランを開始します
-- 既存のレコードから列 ID を取得します。
@Column_ID を int として宣言します
@Column_Depth を int として宣言します
Tb_Column から @Column_ID = Max(Column_ID) を選択します
@Column_ID が Null でない場合
@Column_ID = @Column_ID+1 を設定します
それ以外
@Column_ID = 1 を設定
-- それがトップレベルの列であるかどうかを判断し、その Column_Path と Column_Order を設定します。
@Column_Path を nvarchar(1000) として宣言します
@Column_Order を int として宣言します
IF @親ID = 0
始める
@Column_Path =Ltrim(Str(@Column_ID)) を設定します
Tb_Column から @Column_Order = Max(Column_Order) を選択します
@Column_Order が Null でない場合
@Column_Order = @Column_Order + 1 を設定します。
Else -- レコードが見つからない場合、これが最初のレコードであることを意味します
@Column_Order = 1
--深さ
を設定します。
@Column_Depth = 1 を設定します
終わり
それ以外
始める
-- 親ノードのパスと深さを取得します。
Tb_Column から @Column_Path = Column_Path 、@Column_Depth = Column_Depth を選択します
@Column_Path が Null の場合
始める
@Err = 1 を設定します
ゴット・ジ・エンド
End
-- 同じ親ノードの下の最大シーケンス番号を取得します。
@Column_Order = Max(Column_Order) From Tb_PicColumn Where Column_Path like
''+@Column_Path+'|%'または Column_ID = @Parent_ID を
選択してください
IF @Column_Order Is Not Null -- シーケンス番号が存在する場合、シーケンス番号の後のすべてのシーケンス番号に 1 を追加します。
始める
-- 挿入される現在のノード以降のすべてのノードのシーケンス番号を更新します。
Tb_Column を更新します。 Column_Order = Column_Order +1 を設定します。 ここで、Column_Order
>@Column_Order
-- 同じ親ノードの下の最大シーケンス番号に 1 を加えた値が、独自のシーケンス番号を形成します。
@Column_Order = @Column_Order + 1 を設定します。
終わり
それ以外
始める
@Err=1 を設定します
ゴット・ジ・エンド
End
-- 親ノードのパスとその独自の ID 番号が独自のパスを形成します。
@Column_Path = @Column_Path + '|' + Ltrim(Str(@Column_ID))
--Depth を
設定します
@Column_Depth = @Column_Depth+1
を
設定して、Tb_Column(Column_ID,Column_Name,Parent_ID,Column_Path,
Column_Depth,Column_Order,Column_Intro) Values(@Column_ID,@Column_Name,@Parent_ID,@Column_Path,@Column_Depth,@Column _Order,@Column_Intro) に挿入します。 )
IF @@エラー<>0
始める
@Err=1 を設定します
ゴット・ジ・エンド
End
-- 現在のレコードの後のレコードの ORDER を更新します。
--Tb_Column を更新します。 Column_Order = Column_Order+1 を設定します。 Column_Order > @Column_Order
theEnd:
IF @エラー=0
始める
コミットトランス
@Column_ID を返す
終わり
それ以外
始める
ロールバックトラン
0を返す
終わり
行く
================================================ ============================
ストアド プロシージャ 2: カテゴリの削除
CREATE PROCEDURE sp_Column_Delete
(
@Column_ID int
)
として
@Err As int を宣言する
@Err = 0 を設定します
トランスの開始
--まず、ノードの下に子ノードがあるかどうかを確認します。
Tb_Column から Column_ID を選択します (Parent_ID = @Column_ID)
IF @@RowCount<>0
始める
@Err = 1 を設定します
ゴット・ジ・エンド
End
-- 削除後に他のレコードの順序を並べ替えるために、ノードの Column_Order を取得します。
@Column_Order を int として宣言します
Tb_Column から @Column_Order = Column_Order を選択します (Column_ID = @Column_ID)。
@Column_Order が NUll の場合
始める
@Err =2 を設定します
ゴット・ジ・エンド
終了
-- 他のレコードの Column_Order を更新します。
Tb_Column を更新します。 Column_Order = Column_Order -1 を設定します。 ここで、Column_Order >@Column_Order
IF @@エラー<>0
始める
@Err =3 を設定します
ゴット・ジ・エンド
--delete 操作の
終了
Column_ID=@Column_IDの Tb_Column から削除
IF @@エラー<>0
始める
@Err =4 を設定します
ゴット・ジ・エンド
終了
-- 他のレコードの Column_ID を更新します。
--Tb_Column の更新 Column_ID= Column_ID - 1 Where Column_ID >@Column_ID
--IF @@エラー<>0
- 始める
-- @Err =5 を設定します
-- ゴット・ジ・エンド
-- 終了
theEnd:
IF @エラー = 0
始める
コミットトランス
0 を返す -- 正常に削除されました
終わり
それ以外
始める
IF @エラー=1
始める
ロールバックトラン
戻り値 1 -- 子ノードがあります
終わり
それ以外
始める
ロールバックトラン
戻り値 2 -- 不明なエラー
終わり
終わり
行く
================================================= ================
ストアド プロシージャ 3: 分類の編集
プロシージャの作成 sp_Column_Update
(
@Column_ID int,
@Parent_ID int,
@Column_Name nvarchar(50),
@Column_Intro nvarchar(1000)
)
として
@Err As int を宣言する
Set @Err=0
Begin Tran
-- 変更前の値を取得します: Parent_ID、Column_Depth、Column_Order
@oParent_ID を int として宣言します
@oColumn_Depth を int として宣言します
@oColumn_Order を int として宣言します
@oColumn_Path を nvarchar(1000) として
宣言 @oParent_ID = Parent_ID、@oColumn_Depth = Column_Depth、@oColumn_Order = Column_Order、@oColumn_Path = Column_Path From Tb_Column Where Column_ID = @Column_ID
@oParent_ID が Null の場合
始める
@Err = 1 を設定します
ゴット・ジ・エンド
終了
-- 親 ID が変更されていない場合は、列名と列の紹介を直接変更します。
IF @oParent_ID = @Parent_ID
始める
Tb_Column を更新します。 Column_Name = @Column_Name,Column_Intro = @Column_Intro を設定します。 Column_ID = @Column_ID
IF @@エラー <> 0
@Err = 2 を設定します
ゴット・ジ・エンド
終わり
@nColumn_Path を nvarchar(1000) として宣言します
@nColumn_Depth を int として宣言します
Declare @nColumn_Order As int
-- 現在のノードに含まれるノードの数を親ノードとして取得します (それ自体を含む) 注: "1" が返された場合、それは単一ノードであることを意味します
@theCount を int として宣言します
@theCount = Count(Column_ID) From Tb_Column Where Column_ID=@Column_IDまたは Column_Path (''+@oColumn_Path+'|%'など) を選択してください
@theCount が Null の場合
始める
@Err = 3 を設定します
ゴット・ジ・エンド
End
IF @Parent_ID=0 -- 最上位ノードとして設定されている場合、ノードを最後の最上位ノードに設定します
始める
--Print 'トップレベルの列として設定'
@nColumn_Path = Ltrim(Str(@Column_ID)) を設定します
@nColumn_Depth =1 を設定
@nColumn_Order = Max(Column_Order) From Tb_Column を選択
@nColumn_Order が NULL の場合
始める
@Err = 4 を設定します
ゴット・ジ・エンド
End
Set @nColumn_Order = @nColumn_Order - @theCount + 1
--3 つの部分を更新 1 ノード自体 2 すべての子ノード 2 このツリーが変更される前の後続のレコードの順序
--Print 'この列の前の位置以降のすべての列を更新します (この列の下のサブ列を除く): Column_Order'
Tb_Column を更新します。 Set Column_Order = Column_Order-@theCount Where (Column_Order >@oColumn_Order) And (Column_Path Not like ''+@oColumn_Path+'|%' )
IF @@エラー <> 0
始める
@Err = 7 を設定します
ゴット・ジ・エンド
End
--Print 'この列を更新します: Parent_ID、Column_Path、Column_Depth、Column_Order、Column_Name、Column_Intro'
'順序を印刷: '+Ltrim(Str(@nColumn_Order))
Tb_Column を更新 Set Parent_ID=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth,Column_Order = @nColumn_Order, Column_Name = @Column_Name,Column_Intro = @Column_Intro Column_ID = @Column_ID
IF @@エラー <> 0
始める
@Err = 5 を設定します
ゴット・ジ・エンド
End
--Print 'この列の下のすべてのサブ列を更新します: Column_Path、Column_Depth、Column_Order'
Update Tb_Column Set Column_Path = Replace(Column_Path,@oColumn_Path,@nColumn_Path),Column_Depth = Column_Depth + (@nColumn_Depth-@oColumn_Depth),Column_Order = Column_Order+( @nColumn_Order-@oColumn_Order) Where Column_Path like ''+@oColumn_Path+'|%'
IF @@エラー <> 0
始める
@Err = 6 を設定します
ゴット・ジ・エンド
終わり
終わり
それ以外
始める
-- 将来の親ノードの関連情報を取得し、このノードの関連値を設定します
@nColumn_Depth = Column_Depth、@nColumn_Path = Tb_Column からの Column_Path を選択します (Column_ID = @Parent_ID)。
@nColumn_Depth が NULL または @nColumn_Path が NULL の場合
始める
@Err = 8 を設定します
ゴット・ジ・エンド
終わり
@nColumn_Depth = @nColumn_Depth +1 を設定します
Tb_Column から @nColumn_Order =Max(Column_Order) を選択します。ここで、Column_ID = @Parent_ID または''+@nColumn_Path+'|%'のような Column_Path
@nColumn_Order が NULL の場合
始める
@Err = 9 を設定します
ゴット・ジ・エンド
End
Set @nColumn_Path = @nColumn_Path +'|'+ Ltrim(Str(@Column_ID))
IF @nColumn_Order = @oColumn_Order+1 -- 新しい親ノードが元の位置より上の最も近い兄弟である場合、すべてのノードの順序は次のようになります。変わってください。
始める
Tb_Column を更新 Set Parent_ID=@Parent_ID、Column_Path = @nColumn_Path、Column_Depth = @nColumn_Depth、Column_Name = @Column_Name、Column_Intro = @Column_Intro Column_ID = @Column_ID
IF @@エラー <> 0
始める
@Err = 10 を設定します
ゴット・ジ・エンド
終わり
End
Set @nColumn_Order = @nColumn_Order + 1
-- 3 つの部分を更新します 1 このツリーが変更される前の次の (または前の) レコードの順序 1 ノード自体 3 すべての子ノード
--上昇または下降に分かれる
--Print 'この列の前の位置 [またはこの列の後の位置] 以降のすべての列を更新します (この列の下のサブ列を除く): Column_Order'
IF @nColumn_Order < @oColumn_Order
始める
Tb_Column の更新 Set Column_Order = Column_Order+@theCount Where Column_Order<@oColumn_Order And Column_Order >=@nColumn_Order And (Column_Path Not like ''+@oColumn_Path+'|%' ) And Column_ID<>@Column_ID
IF @@エラー <> 0
始める
@Err = 12 を設定します
ゴット・ジ・エンド
終わり
終わり
それ以外
始める
Tb_Column の更新 Set Column_Order = Column_Order-@theCount Where Column_Order >@oColumn_Order And Column_Order<@nColumn_Order And (Column_Path Not like ''+@oColumn_Path+'|%' ) And Column_ID<>@Column_ID
IF @@エラー <> 0
始める
@Err = 13 を設定します
ゴット・ジ・エンド
終わり
End
--Print 'この列を更新します: Parent_ID、Column_Path、Column_Depth、Column_Order、Column_Name、Column_Intro'
'順序を印刷: '+Ltrim(Str(@nColumn_Order))
IF @nColumn_Order > @oColumn_Order
@nColumn_Order = @nColumn_Order - @theCount を設定します
Tb_Column を更新 Set Parent_ID=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth,Column_Order = @nColumn_Order, Column_Name = @Column_Name,Column_Intro = @Column_Intro Column_ID = @Column_ID
IF @@エラー <> 0
始める
@Err = 10 を設定します
ゴット・ジ・エンド
End
--Print 'この列の下のすべてのサブ列を更新します: Column_Paht、Column_Depth、Column_Order'
Update Tb_Column Set Column_Path = Replace(Column_Path,@oColumn_Path,@nColumn_Path),Column_Depth = Column_Depth + (@nColumn_Depth-@oColumn_Depth),Column_Order = Column_Order+(@nColumn_Order-@oColumn_Order) Where Column_Path like ''+@oColumn_Path+'|%'
IF @@エラー <> 0
始める
@Err = 11 を設定します
ゴット・ジ・エンド
終わり
終わりの
終わり
:
IF @Err<>0 -- エラーがある場合は、エラー番号を返します。
始める
ロールバックトラン
@Err を返す
終わり
Else -- エラーがない場合は 0 を返します。
始める
コミットトランス
0を返す
終わり
行く
================================================= =========================
ストアド プロシージャ 4: 分類の表示 (単なる select ステートメント)
カテゴリリスト:
CREATE PROCEDURE sp_Column_List
として
SELECT 列 ID、列名、親 ID、列パス、列深さ、
列の順序、列のイントロ
FROM Tb_Column
ORDER BY 列_順序
行く
=======================================
以下は、友人のフォーラムに投稿された、ASP.NET での使用例です。
http://www.mzline.com/bbs/dispbbs.asp?boardID=67&ID=5044&replyID=25788&star=1&skin=0#25788