0

私は再帰的CTEを初めて使用します。私は、各マネージャーの名前ですべての従業員を返すCTEを開発しようとしています。だから私は2つのテーブルを持っています:people_rvそしてstaff_rv

People_rvテーブルには、マネージャーと従業員の両方のすべての人が含まれています。Staff_rvマネージャー情報のみが含まれます。Uniqueidentifierスタッフの値はに格納されStaff_rvます。Uniqueidentifierの従業員の値はに格納されpeople_rvます。 People_rvマネージャーと従業員の両方のvarcharの名前と名前の値が含まれます。

しかし、次のCTEを実行すると、エラーが発生します。

WITH
cteStaff (ClientID, FirstName, LastName, SupervisorID, EmpLevel)
AS
(
    SELECT p.people_id, p.first_name, p.last_name, s.supervisor_id,1
    FROM people_rv p JOIN staff_rv s on s.people_id = p.people_id
    WHERE s.supervisor_id = '95E16819-8C3A-4098-9430-08F0E3B764E1' 
    UNION ALL
    SELECT p2.people_id, p2.first_name, p2.last_name, s2.supervisor_id, r.EmpLevel + 1
    FROM people_rv p2 JOIN staff_rv s2 on s2.people_id = p2.people_id
    INNER JOIN cteStaff r on s2.staff_id = r.ClientID
)
SELECT
    FirstName + ' ' + LastName AS FullName, 
    EmpLevel,
    (SELECT first_name + ' ' + last_name FROM people_rv p join staff_rv s on s.people_id = p.people_id 
    WHERE s.staff_id = cteStaff.SupervisorID) AS Manager
FROM cteStaff
OPTION (MAXRECURSION 0);

私の出力は次のとおりです。

Barbara G   1   Melanie K
Dawn P  1   Melanie K
Garrett M   1   Melanie K
Stephanie P 1   Melanie K
Amanda F    1   Melanie K
Amanda T    1   Melanie K
Stephanie G 1   Melanie K
Carlos H    1   Melanie K

したがって、それは最初のレベル以上の反復ではありません。なぜだめですか?メラニーは一番上の監督者ですが、左端の列の各人も監督者です。したがって、このクエリもレベル2を返す必要があります。

4

4 に答える 4

3

参加すると無限ループに陥る可能性があります。テーブルが実際に下がると予想されるレベル数を確認します。一般的に、あなたは同じようなことで再帰に参加します

 ID = ParentID

テーブルまたは式に含まれているもののいずれか。関係を構築する必要がある場合は、再帰CTEの前にCTEを作成することもできることに注意してください。

これは自己実行する例です、それは役立つかもしれません。

Declare @table table ( PersonId int identity, PersonName varchar(512), Account int, ParentId int, Orders int);

insert into @Table values ('Brett', 1, NULL, 1000),('John', 1, 1, 100),('James', 1, 1, 200),('Beth', 1, 2, 300),('John2', 2, 4, 400);

select 
    PersonID
,   PersonName
,   Account
,   ParentID
from @Table

; with recursion as 
    (
    select 
        t1.PersonID
    ,   t1.PersonName
    ,   t1.Account
    --, t1.ParentID
    ,   cast(isnull(t2.PersonName, '')
            + Case when t2.PersonName is not null then '\' + t1.PersonName else t1.PersonName end
            as varchar(255)) as fullheirarchy
    ,   1 as pos
    ,   cast(t1.orders + 
            isnull(t2.orders,0) -- if the parent has no orders than zero
            as int) as Orders
    from @Table t1
        left join @Table t2 on t1.ParentId = t2.PersonId
    union all
    select 
        t.PersonID
    ,   t.PersonName
    ,   t.Account
    --, t.ParentID
    ,   cast(r.fullheirarchy + '\' + t.PersonName as varchar(255))
    ,   pos + 1  -- increases
    ,   r.orders + t.orders
    from @Table t
        join recursion r on t.ParentId = r.PersonId
    )
, b as 
    (
    select *, max(pos) over(partition by PersonID) as maxrec  -- I find the maximum occurrence of position by person
    from recursion
    )
select *
from b
where pos = maxrec  -- finds the furthest down tree
-- and Account = 2  -- I could find just someone from a different department
于 2013-03-08T21:25:05.943 に答える
3

私が知る限り、あなたの問題は、マネージャーと従業員をつなぐ結合がないことです。

この結合

INNER JOIN cteStaff r on r.StaffID = s2.staff_id

同じ最初のレベル 1 スタッフを自分に戻すだけです。

アップデート:

まだ完全ではありません!Supervisor_id がありますが、実際にはそれを使用して CTE に戻っていません。

したがって、この CTE の再帰ごとに、次のことを行う必要があります (名前の結合を除く)。

select {Level 1 Boss}, NULL (no supervisor)
union
select {new employee}, {that employee's boss}

そのため、join はCTEのClientID (レベル 1 ボス) を 2 番目の UNION クエリの Supervisor フィールドに接続する必要あります。

この 2 番目のタスクを達成するための JOIN は次のとおりです (staff_rv テーブル スキーマからわかることから):

 SELECT p2.people_id, p2.first_name, p2.last_name, s2.supervisor_id, r.EmpLevel + 1
    FROM people_rv p2 JOIN staff_rv s2 on s2.people_id = p2.people_id
    INNER JOIN cteStaff r on s2.supervisor_id = r.ClientID

一番下の結合は、r.ClientID (レベル 1 のボス) をスタッフの Supervisor_id フィールドに結合していることに注意してください。

(NB: あなたの staff_id と Supervisor_id は people_rv テーブルの people_id の値を模倣していると思うので、この結合はうまくいくはずです。しかし、それらが異なる場合 (つまり、スタッフの Supervisor_id がそのスーパーバイザの people_id ではない)、次のように記述する必要があります。スタッフの Supervisor_id を、CTE に ClientID として保存している people_id に結合できるように結合します。)

于 2013-03-08T19:47:34.087 に答える
2

これは、レビューするのに適した単純な再帰 CTE です (これは答えではないかもしれませんが、再帰 CTE を作成する方法を探している他の誰かがそれを必要とするかもしれません)。

-- Recursive CTE
;
WITH    Years ( myYear )
          AS (
               -- Base case
      SELECT    DATEPART(year, GETDATE())
               UNION ALL
      -- Recursive
               SELECT   Years.myYear - 1
               FROM     Years
               WHERE    Years.myYear >= 2002
             )
    SELECT  *
    FROM    Years
于 2013-03-08T21:31:15.863 に答える
1

これはおそらく問題を解決しないことに注意してください。ただし、元のクエリでどこが間違っているかを確認する手段になることを願っています。

デフォルトは 100 レベルの再帰MAXRECURSIONです。CTE から選択するクエリ ヒントを使用して無制限に設定できます。

...
FROM cteStaff
OPTION (MAXRECURSION 0);

MSDNから:

MAXRECURSION 数

このクエリで許可される再帰の最大数を指定します。number は、0 から 32767 までの負でない整数です。0 を指定すると、制限は適用されません。このオプションが指定されていない場合、サーバーのデフォルトの制限は 100 です。

クエリの実行中に MAXRECURSION 制限の指定された数またはデフォルトの数に達すると、クエリは終了し、エラーが返されます。

このエラーのため、ステートメントのすべての効果がロールバックされます。ステートメントが SELECT ステートメントの場合、部分的な結果が返されるか、結果が返されない場合があります。返される部分的な結果には、指定された最大再帰レベルを超える再帰レベルのすべての行が含まれない場合があります。

于 2013-03-08T19:35:38.473 に答える