Рекурсивный запрос - выберите только те узлы, где конечные узлы представляют активные данные. - PullRequest
5 голосов
/ 03 марта 2010

Учитывая следующий рекурсивный запрос:

WITH DepartmentHierarchy (DepartmentID, Name, IsInactive, IsSpecial, ParentId, HierarchyLevel) AS
(
   -- Base case
   SELECT
      DepartmentId,
      Name,
      IsInactive,
      IsSpecial,
      ParentId,
      1 as HierarchyLevel
   FROM StoreDepartment
   WHERE ParentId IS NULL

   UNION ALL

   -- Recursive step
   SELECT
      d.DepartmentId,
       d.Name,
      d.IsInactive,
      d.IsSpecial,
      d.ParentId,
      dh.HierarchyLevel + 1 AS HierarchyLevel
   FROM StoreDepartment d
      INNER JOIN DepartmentHierarchy dh ON
         d.ParentId = dh.DepartmentId
) SELECT * FROM DepartmentHierarchy 

Я могу выбрать данные, которые выглядят так:

DepartmentId, Name, IsInactive, IsSpecial, ParentId, HeirarchyLevel
1, Store, 0, 0, NULL, 1
2, Main Department 1, 0, 1, 2
3, Main Department 2, 0, 1, 2
4, Sub For Main 1, 0, 2, 3

Также предположим, что существует таблица с DepartmentId и ItemId (например, DepartmentItemRelationship). Узлы листьев из иерархии отдела здесь соединены с элементами.

Я хочу, чтобы мой рекурсивный запрос возвращал только узлы (на любом уровне), у которых под ними есть хотя бы один листовой узел с соответствием в таблице отношений отдел / элемент. Эти узлы могут быть на 6 или 7 уровней ниже, поэтому я не уверен, как бы я изменил свой запрос, чтобы обязательно включить их.

Спасибо, Kyle

Ответы [ 2 ]

3 голосов
/ 03 марта 2010

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

Попробуйте что-то вроде этого:

    WITH DepartmentHierarchy (DepartmentID, Name, IsInactive, IsSpecial, ParentId, HierarchyLevel) AS
(
   -- Base case
   SELECT
      '/'+cast( DepartmentId as varchar(max)) as [path]
      DepartmentId,
      Name,
      IsInactive,
      IsSpecial,
      ParentId,
      1 as HierarchyLevel
   FROM StoreDepartment
   WHERE ParentId IS NULL

   UNION ALL

   -- Recursive step
   SELECT
      dh.[path] +'/'+ cast( d.DepartmentId as varchar(max)) as [path]
      d.DepartmentId,
      d.Name,
      d.IsInactive,
      d.IsSpecial,
      d.ParentId,
      dh.HierarchyLevel + 1 AS HierarchyLevel
   FROM StoreDepartment d
      INNER JOIN DepartmentHierarchy dh ON
         d.ParentId = dh.DepartmentId
   where exists ( select top 1 1 
                  from DepartmentItemRelationship di
                  where di.DepartmentId = d.DepartmentId )
) 
SELECT * 
FROM DepartmentHierarchy dh
where exists ( select top 1 1 
               from DepartmentHierarchy 
               where charindex('/'+dh.DepartmentID+'/',[path]) > 0) 
1 голос
/ 03 марта 2010

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

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

WITH LeafNodeParents AS
(
    SELECT DISTINCT ParentId
    FROM StoreDepartment
    WHERE DepartmentId NOT IN
    (
        SELECT DISTINCT ParentId FROM StoreDepartment
    )
)
SELECT d.DepartmentId, d.Name, d.IsInactive, d.IsSpecial, d.ParentId
FROM LeafNodeParents p
INNER JOIN StoreDepartment d
    ON d.DepartmentId = p.ParentId

Единственное, что вам не скажут, это уровень. Я не уверен, как сильно вам это нужно. Если вы этого не сделаете, это должно работать лучше, чем рекурсивная версия; если вы это сделаете, похоже, что запрос Хосе в порядке (судя по быстрому взгляду).

...