Как превратить эту операцию курсора в операцию на основе множеств в иерархии замыканий? - PullRequest
0 голосов
/ 26 мая 2019
  1. Trees.DirectReports - это таблица замыкания (иерархии).
  2. Существует таблица с именем Users с: RowID, EmployeeId, and MangerId.@RowCount - это количество записей в этой таблице, а #EmpMgr - курсор для этой таблицы.

Ниже приведен соответствующий SQL-код, который я хотел бы преобразовать из операции на основе курсора воперация на основе множеств.

WHILE @RowCount <= @NumberRecords --loop through each record in Users table
BEGIN       
    SET @EmpId = (SELECT EmployeeId FROM #EmpMgr WHERE RowID = @RowCount)
    SET @MgrId = (SELECT ManagerId FROM #EmpMgr WHERE RowID = @RowCount)

    INSERT INTO [Trees].[DirectReports](EmployeeId, ManagerId, Depth)
    SELECT c.EmployeeId, p.ManagerId, p.Depth + c.Depth + 1
    FROM Trees.DirectReports p join Trees.DirectReports c
    WHERE p.EmployeeId = @MgrId AND c.ManagerId = @EmpId

    SET @RowCount = @RowCount + 1
END

Так что я бы очень хотел выяснить, как сделать это как запрос с множеством, потому что я знаю, что это будет намного быстрее, но мой мозг не совсем правильносоединений сегодня, чтобы выяснить это.

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

1 Ответ

2 голосов
/ 27 мая 2019

Нашел то, что искал с помощью пары других постов. Основной ответ таков:

WITH cte AS
(
    SELECT LegacyId ancestor, LegacyId descendant, 0 depth FROM Users
    UNION ALL

    SELECT cte.ancestor, u.LegacyId descendant, cte.depth + 1 depth
    FROM   dbo.Users u JOIN cte ON u.ManagerId = cte.descendant
)
select * from cte

Однако сначала меня поразило то, что некоторые плохие данные вызывали циклические зависимости. Мне удалось использовать следующий запрос, чтобы определить, где были эти экземпляры:

with cte (id,pid,list,is_cycle) 
as
(
    select      legacyid id, managerid pid,',' + cast (legacyid as varchar(max))  + ',',0
    from        users

    union all

    select      u.legacyid id, 
                u.managerid pid, 
                cte.list + cast(u.legacyid as varchar(10)) +  ',' ,case when cte.list like '%,' + cast (u.legacyid as varchar(10)) + ',%' then 1 else 0 end
    from        cte join users u on u.managerid = cte.id
    where       cte.is_cycle = 0
)
select      *
from        cte
where       is_cycle = 1

Как только я исправил циклические данные, все заработало отлично. Прочтите следующие сообщения SO для получения дополнительной информации, поскольку именно это я и использовал для своего решения: Есть ли способ обнаружить цикл в иерархических запросах в SQL Server? и Как я могу создать таблицу закрытия, используя данные из списка смежности?

...