再帰的 CTE で生成された最後のレコードを取得するにはどうすればよいでしょうか?
-
09-06-2019 - |
質問
以下のコードでは、SQL Server 2005 の再帰 CTE (共通テーブル式) を使用して、基本的な階層構造の最上位の親を見つけようとしています。この階層のルールは、すべての CustID に ParentID があり、CustID に親がない場合は ParentID = CustID となり、それが最上位になります。
DECLARE @LookupID int
--Our test value
SET @LookupID = 1
WITH cteLevelOne (ParentID, CustID) AS
(
SELECT a.ParentID, a.CustID
FROM tblCustomer AS a
WHERE a.CustID = @LookupID
UNION ALL
SELECT a.ParentID, a.CustID
FROM tblCustomer AS a
INNER JOIN cteLevelOne AS c ON a.CustID = c.ParentID
WHERE c.CustID <> a.CustomerID
)
したがって、tblCustomer が次のようになった場合:
ParentID CustID
5 5
1 8
5 4
4 1
上記のコードから得られる結果は次のとおりです。
ParentID CustID
4 1
5 4
5 5
私が欲しいのは、その結果の最後の行だけです。
ParentID CustID
5 5
CTE で生成された最後のレコード (最高レベルの CustID) を返すにはどうすればよいですか?
また、このテーブルには無関係な CustID 階層が複数あるため、単に SELECT * FROM tblCustomer WHERE ParentID = CustID を実行することはできないことにも注意してください。ID 番号は階層内のどこにあるかとは関係がないため、ParentID または CustID で注文することはできません。
解決
最も高い再帰深度が必要な場合は、次のようなことはできませんか?その後、実際に CTE にクエリを実行するときに、max(Depth) を持つ行を探すだけでよいでしょうか?そのようです:
DECLARE @LookupID int
--Our test value
SET @LookupID = 1;
WITH cteLevelOne (ParentID, CustID, Depth) AS
(
SELECT a.ParentID, a.CustID, 1
FROM tblCustomer AS a
WHERE a.CustID = @LookupID
UNION ALL
SELECT a.ParentID, a.CustID, c.Depth + 1
FROM tblCustomer AS a
INNER JOIN cteLevelOne AS c ON a.CustID = c.ParentID
WHERE c.CustID <> a.CustID
)
select * from CTELevelone where Depth = (select max(Depth) from CTELevelone)
または、trevor が示唆していることを応用すると、これを同じ CTE で使用できます。
select top 1 * from CTELevelone order by Depth desc
あなたが説明したケースでは、CustomerID が必ずしも注文に使用したかったものではないと思いますが、私も質問について完全に明確ではありませんでした。
他のヒント
問題を完全に理解しているかどうかはわかりませんが、ハック&スラッシュを試すために、次のことを試してみてください。
SELECT TOP 1 FROM cteLevelOne ORDER BY CustID DESC
これは、GUID のようなものではなく、CustID も例のように適切であることを前提としています。
まず、親の子のいずれかが同じである場合、cte は終了しません。これは再帰的な CTE であるため、終了する必要があります。Parent ID と cust id が同じであると、ループは終了しません。
MSG 530、レベル16、状態1、行15ステートメントが終了しました。ステートメントが完了する前に、最大再帰回数 100 が使い果たされました。