Item_ID、Item_ParentID の通常のツリー構造を持つ SQL サーバーのテーブルがあります。特定の Item_ID (任意のレベル) のすべての CHILDREN を反復して取得したいとします。
再帰はこの問題の直観的な候補のようで、これを行う SQL Server 関数を作成できます。
テーブルに多くのレコードがある場合、これはパフォーマンスに影響しますか? 再帰を避けて単純にテーブルをクエリするにはどうすればよいですか? 提案をお願いします。
Item_ID、Item_ParentID の通常のツリー構造を持つ SQL サーバーのテーブルがあります。特定の Item_ID (任意のレベル) のすべての CHILDREN を反復して取得したいとします。
再帰はこの問題の直観的な候補のようで、これを行う SQL Server 関数を作成できます。
テーブルに多くのレコードがある場合、これはパフォーマンスに影響しますか? 再帰を避けて単純にテーブルをクエリするにはどうすればよいですか? 提案をお願いします。
一般的な答えとして、反復アルゴリズムを使用するだけで、通常は再帰が必要なかなり洗練された処理を SQL Server で行うことができます。Transact SQL で XHTML パーサーを実行することができましたが、これは驚くほどうまく機能しました。私が書いたコードプリティファイアは、ストアドプロシージャで行われました。それはエレガントではなく、バッファローがバレエをしているのを見ているようなものです. しかし、それは動作します。
SQL 2005 を使用していますか?
その場合は、共通テーブル式を使用できます。これらの行に沿ったもの:
;
with CTE (Some, Columns, ItemId, ParentId) as
(
select Some, Columns, ItemId, ParentId
from myTable
where ItemId = @itemID
union all
select a.Some, a.Columns, a.ItemId, a.ParentId
from myTable as a
inner join CTE as b on a.ParentId = b.ItemId
where a.ItemId <> b.ItemId
)
select * from CTE
Joe Celko は、特に SQL データベースのツリー構造に関する本(<- Amazon へのリンク) を持っています。モデルに再帰が必要であり、パフォーマンスの問題が発生する可能性は間違いありませんが、特定の問題に関係するものに応じて、ツリー構造をモデル化する別の方法があり、再帰を回避してパフォーマンスを向上させることができます。
再帰とパフォーマンスで直面する問題は、結果を返すために何回再帰しなければならないかということです。各再帰呼び出しは、合計結果に結合する必要がある別の個別の呼び出しです。
SQL 2k5 では、共通テーブル式を使用してこの再帰を処理できます。
WITH Managers AS
(
--initialization
SELECT EmployeeID, LastName, ReportsTo
FROM Employees
WHERE ReportsTo IS NULL
UNION ALL
--recursive execution
SELECT e.employeeID,e.LastName, e.ReportsTo
FROM Employees e INNER JOIN Managers m
ON e.ReportsTo = m.employeeID
)
SELECT * FROM Managers
または別の解決策は、階層を別のテーブルにフラット化することです
Employee_Managers
ManagerId (PK、従業員テーブルへの外部キー)
EmployeeId (従業員テーブルへの主キー、外部キー)
すべての親子関係はこのテーブルに格納されるため、マネージャー 1 がマネージャー 2 を管理し、従業員 3 を管理する場合、テーブルは次のようになります。
ManagerId EmployeeId
1 2
1 3
2 1
これにより、階層を簡単に照会できます。
select * from employee_managers em
inner join employee e on e.employeeid = em.employeeid and em.managerid = 42
これは、マネージャーが 42 であるすべての従業員を返します。メリットはパフォーマンスの向上ですが、デメリットは階層の維持です。
おそらく、もう少し詳細が整っているでしょう。
あなたが説明したようにマスターと詳細の関係がある場合、単純な JOIN では必要なものが得られませんか?
次のように:
SELECT
SOME_FIELDS
FROM
MASTER_TABLE MT
,CHILD_TABLE CT
WHERE CT.PARENT_ID = MT.ITEM_ID
childrenの再帰は必要ありません- 真下のレベル (つまり ) だけを見ている- すべての子孫select * from T where ParentId = @parent
の再帰のみが必要です。
SQL2005 では、次の方法で子孫を取得できます。
with AllDescendants (ItemId, ItemText) as (
select t.ItemId, t.ItemText
from [TableName] t
where t.ItemId = @ancestorId
union
select sub.ItemId, sub.ItemText
from [TableName] sub
inner join [TableName] tree
on tree.ItemId = sub.ParentItemId
)
再帰はまったく必要ありません....入力を簡単にするために、列をItemIDとItemParentIDに変更したことに注意してください...
DECLARE @intLevel INT SET @intLevel = 1INSERT INTO TempTable(ItemID、ItemParentID、レベル) SELECT ItemID、ItemParentID、@intLevel ItemParentID が NULL の場合
WILE @intLevel < @TargetLevel 始める SET @intLevel = @intLevel + 1 INSERT INTO TempTable(ItemID、ItemParentID、レベル) SELECT ItemID、ItemParentID、@intLevel WHERE ItemParentID IN (TempTable から ItemID を選択 WHERE レベル = @intLevel-1) -- 行が挿入されない場合、子はありません IF @@ROWCOUNT = 0 壊す 終わり
SELECT ItemID FROM TempTable WHERE レベル = @TargetLevel