2016-09-06 20 views
2

「MurachのSQL Server 2016開発者向け」の本の例があります。この例は、SQLで再帰CTSをコード化する方法を示しています。 Imは再帰関数(C#で)をよく知っていますが、SQL再帰ロジックの仕組みを何とか理解できません。再帰クエリCTE

USE Examples; 

WITH EmployeesCTE AS 
(
     -- Anchor member 
     SELECT EmployeeID, 
      FirstName + ' ' + LastName As EmployeeName, 
      1 As Rank 
     FROM Employees 
     WHERE ManagerID IS NULL 
    UNION ALL 
     -- Recursive member 
     SELECT Employees.EmployeeID, 
      FirstName + ' ' + LastName, 
      Rank + 1 
     FROM Employees 
      JOIN EmployeesCTE 
      ON Employees.ManagerID = EmployeesCTE.EmployeeID 
) 
SELECT * 
FROM EmployeesCTE 
ORDER BY Rank, EmployeeID; 

このクエリは、組織内の従業員の階層レベルを返します。 はここでの例です。

私の質問:再帰関数では、(ベースケースに到達することによって)再帰を終了する減分変数が表示されます。私の質問は、EmployeesCTEの対応する部分はどこですか?論理を理解するのを手伝ってください。

+0

ここで、 'INNER JOIN'はフィルタとして機能し、基礎となるテーブルには循環参照がない限り、その" ON "条件サーバを"ターミネータ "として機能します。 –

+0

経験の浅いので、大規模なデータではパフォーマンスが悪くなることに注意してください。あるいは、RootManagerIDをEmployeesテーブルに追加することを検討してください。次に、「SELECT EmployeeID、ManagerID、FirstName + '' + LastName As EmployeesName From Employees WHERE RootManagerID = @ RootManagerID'」を選択します。次に、アプリケーションレベルでツリーを構築します。このアプローチの大きなパフォーマンス上の利点の他に、 'RootManagerID'にインデックスを作成することでパフォーマンスをさらに向上させることもできます。 – yazanpro

+0

@yazanpro、これは*大量のデータに悪い* *隠されたRBAR *であるために悪くなります* ... – Shnugo

答えて

2

「再帰CTE」とは、実際には反復CTEと呼ばれるものです。アイデアは、再帰テーブル(この場合はEmployeesCTE)を定義するために、我々はいくつかの初期の行を作成することで開始することで、この場合には、これは

SELECT EmployeeID, 
     FirstName + ' ' + LastName As EmployeeName, 
     1 As Rank 
    FROM Employees 
    WHERE ManagerID IS NULL 

によって行われます(これがそうEmployeesCTEへの参照が含まれていないことに気付きます再帰的ではありません)、この場合、表現を繰り返します。この場合は、さらにいくつかの行を生成するために、式を繰り返します(この場合は

SELECT Employees.EmployeeID, 
     FirstName + ' ' + LastName, 
     Rank + 1 
    FROM Employees 
     JOIN EmployeesCTE 
     ON Employees.ManagerID = EmployeesCTE.EmployeeID 

)。その式が行を返さなくなるまで、これを行います。この式では、EmployeesCTEはそのテーブルの前のバージョンを参照し、それを評価することによって、そのテーブルの次のバージョンを計算します。

したがって、再帰式(または反復)を停止する条件は、再帰式が新しい行を生成しなかったことです。

ここで、あなたが与えた特定の例に上記のすべてがどのように適用されるかを見てみましょう。私たちの最初の行セットは、マネージャーを持たない従業員(私たちはそれらをランク1の従業員と呼ぶ)で構成されています。次に、前の手順で見つかった従業員によって管理されているすべての従業員(私たちは2人の従業員と呼んでいます)を見つけます。次に、従業員は2番目の従業員によって管理され、3番目の従業員などが呼び出されます。最終的には、新しい従業員が見つからない段階に到達します(もちろん、関係によって管理されるのはサイクルがないと仮定します)。

1

あなたはC#に精通しているので、複雑なオブジェクトモデルのように考えることができます。

単純なWindows.Forms.Formのコントロールを想像してみてください。各コントロールには、コントロールコレクション自体があります。データベースでは、従業員が階層の上の次の上司を指し示すように、各行がその親行を指す(上位オブジェクトがNULLになる)自己参照テーブルを考えることができます。

メソッドRefresh()を持つトップオブジェクトがあります。これを呼び出すと、関数は独自のコンテンツで何かを行い、内部コレクションのRefresh()を呼び出します。コレクションには、すべてのメンバーのRefresh()が呼び出されます。彼らのすべては何かをして、内部のコレクションにRefresh()と呼んでいます。これは、空のControly-collectionを持つControlsに到達するまで、ネストされたモデルを実行します。

トップダウンカスケードのようなものです。実際には、再帰的なCTEを意図的に停止させるのは非常に難しい場合があります。

JOIN操作が行を返さないとき、再帰CTEの2番目の部分は、自然の終わりに来る...あなたの場合

あなたは

  • アンカーとしてこれを読むことができる:フェッチボス(最高レベル)
  • を持っていないすべての従業員は今、彼らのマネージャーとしてそれらのいずれかを持っているすべての従業員(第2レベル)
  • 行毎ダウン行くためのリストを尋ねるとそのマネージャー
  • ように、第2レベルの人が持っているすべての従業員が応じた従業員はもうなくなるまで

続行ず、再帰CTEがあるという事実を認識してフェッチ - デザインで - 遅いアプローチは、隠されたRBARです。