9

Item_ID、Item_ParentID の通常のツリー構造を持つ SQL サーバーのテーブルがあります。特定の Item_ID (任意のレベル) のすべての CHILDREN を反復して取得したいとします。

再帰はこの問題の直観的な候補のようで、これを行う SQL Server 関数を作成できます。

テーブルに多くのレコードがある場合、これはパフォーマンスに影響しますか? 再帰を避けて単純にテーブルをクエリするにはどうすればよいですか? 提案をお願いします。

4

8 に答える 8

5

WITH新しい MS SQL 2005 では、キーワードを使用できます

この質問、特にこの回答を確認してください。

Oracle では、CONNECT BYキーワードを使用して階層クエリを生成できます ( syntax )。

私の知る限りMySQLでは、再帰を使用する必要があります。

または、レコードの親子関係のキャッシュ テーブルをいつでも作成できます。

于 2008-10-10T13:24:01.030 に答える
2

一般的な答えとして、反復アルゴリズムを使用するだけで、通常は再帰が必要なかなり洗練された処理を SQL Server で行うことができます。Transact SQL で XHTML パーサーを実行することができましたが、これは驚くほどうまく機能しました。私が書いたコードプリティファイアは、ストアドプロシージャで行われました。それはエレガントではなく、バッファローがバレエをしているのを見ているようなものです. しかし、それは動作します。

于 2008-10-10T14:04:00.590 に答える
1

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
于 2008-10-10T13:30:13.277 に答える
1

Joe Celko は、特に SQL データベースのツリー構造に関する(<- Amazon へのリンク) を持っています。モデルに再帰が必要であり、パフォーマンスの問題が発生する可能性は間違いありませんが、特定の問題に関係するものに応じて、ツリー構造をモデル化する別の方法があり、再帰を回避してパフォーマンスを向上させることができます。

于 2008-10-10T14:37:16.107 に答える
1

再帰とパフォーマンスで直面する問題は、結果を返すために何回再帰しなければならないかということです。各再帰呼び出しは、合計結果に結合する必要がある別の個別の呼び出しです。

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 であるすべての従業員を返します。メリットはパフォーマンスの向上ですが、デメリットは階層の維持です。

于 2008-10-10T13:51:56.620 に答える
0

おそらく、もう少し詳細が整っているでしょう。

あなたが説明したようにマスターと詳細の関係がある場合、単純な JOIN では必要なものが得られませんか?

次のように:

SELECT
  SOME_FIELDS
FROM
  MASTER_TABLE MT
 ,CHILD_TABLE CT
WHERE CT.PARENT_ID = MT.ITEM_ID
于 2008-10-10T13:18:19.590 に答える
0

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
)
于 2008-10-10T13:34:57.230 に答える
0

再帰はまったく必要ありません....入力を簡単にするために、列をItemIDとItemParentIDに変更したことに注意してください...

DECLARE @intLevel INT
SET @intLevel = 1

INSERT 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

于 2008-10-10T15:37:57.327 に答える