2013-03-08 2 views
0

Я новичок в рекурсивных CTE. Я пытаюсь разработать CTE, который вернет всех сотрудников под каждым именем менеджера. Поэтому у меня есть две таблицы: people_rv и staff_rvКак разработать рекурсивный CTE в T-SQL?

Таблица People_rv содержит всех людей, как менеджеров, так и сотрудников. Staff_rv содержит только информацию о менеджере. Значения персонала Uniqueidentifier хранятся в Staff_rv. Значения сотрудника Uniqueidentifier хранятся в people_rv. People_rv содержит значения имени и фамилии varchar как для менеджеров, так и для сотрудников.

Но когда я запускаю следующий КТР я получаю сообщение об ошибке:

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 

Так что не итерацию больше, чем первый уровень. Почему нет? Melanie - самый главный руководитель, но каждый из лиц в самом левом столбце также является руководителем. Таким образом, этот запрос должен также возвращать уровень 2.

+0

Вы уверены, что GUID используется в конечной ноге рекурсивного КТЭ правильно? Первое, о чем я думаю, состоит в том, что рекурсивная часть никогда не заканчивается, и первое, что приходит в голову, заключается в том, что условие завершения не выполняется ... и что GUID кажется по крайней мере вероятной целью ... –

+0

Потенциальная опечатка : в INNER JOIN ваш запрос теперь использует 's2.staff_ID', где, похоже, требуется' s2.supervisor_id'? – Hellion

ответ

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 
1

Обратите внимание, что это, вероятно, не решит вашу проблему, но является средством надеяться увидеть, где вы ошибетесь в исходном запросе.

по умолчанию 100 уровней рекурсии - вы можете установить его на неограниченный с помощью подсказки MAXRECURSION запроса, где вы выбираете из вашего КТР:

... 
FROM cteStaff 
OPTION (MAXRECURSION 0); 

От MSDN:

MAXRECURSION number

Specifies the maximum number of recursions allowed for this query. number is a nonnegative integer between 0 and 32767. When 0 is specified, no limit is applied. If this option is not specified, the default limit for the server is 100.

When the specified or default number for MAXRECURSION limit is reached during query execution, the query is ended and an error is returned.

Because of this error, all effects of the statement are rolled back. If the statement is a SELECT statement, partial results or no results may be returned. Any partial results returned may not include all rows on recursion levels beyond the specified maximum recursion level.

+1

Это технически точная информация, но это не основная проблема, если в его таблице нет 101 уровня сотрудников. (Вы работаете в Initech?) –

+0

@KyleHale Я просто хотел рассказать ОП о подсказке - у меня нет окна SQL Server, чтобы проверить запрос ;-) – Bridge

+0

Я только что обновил свое описание. Не могли бы вы посмотреть, пожалуйста? – salvationishere

3

Ваша проблема, насколько я могу судить, заключается в том, что вы не подключаетесь к своим сотрудникам.

Это присоединиться

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

Просто присоединяется тот же самый начальный уровень 1 штатного сотрудника к самому себе.

ОБНОВЛЕНИЕ:

Еще не совсем верно! У вас есть supervisor_id, но опять же вы по-прежнему не используете это, чтобы присоединиться к CTE.

Таким образом, для каждой рекурсии этого КТР вам нужно (за исключением имени присоединиться):

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

Так присоединиться необходимо подключить ClientID КТР в (уровень 1 босс) к руководителю второго UNION запросов Query поле, которое выглядит supervisor_id, а не staff_id.

Соединить для достижения этой цели второй задача (от того, что я могу сказать о вашей 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 босс) в Сотрудница-х superisor_id.

(NB: Я считаю, что ваш staff_id и supervisor_id имитируют ваши значения people_id из таблицы people_rv, поэтому это соединение должно работать нормально. Но если они отличаются (то есть, supervisor_id сотрудника не является файлом people_id), тогда вы будете необходимо написать соединение таким образом, чтобы супервизор сотрудника мог быть присоединен к их people_id, который вы храните как ClientID в CTE.)

+0

Отличный момент, Кайл! Я изменил свой запрос выше и попробовал много вариантов этого, но он не дает мне глубже одного уровня. Можете ли вы определить, где это неправильно? – salvationishere

+0

Я обновил свой ответ. Ваше соединение с профсоюзом немного похоже на мое понимание вашей схемы ... –

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 
Смежные вопросы