Несколько примеров рекурсивных SQL-запросов для системы DIRECTUM

26 2

Чтобы понять рекурсию, нужно сначала понять рекурсию.

Рекурсия — определение, описание, изображение какого-либо объекта или процесса внутри самого этого объекта или процесса, то есть ситуация, когда объект является частью самого себя.

Пожалуй, одним определением и ограничусь, в сети масса информации на эту тему.

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

В стандартной поставке системы DIRECTUM есть ряд справочников, которые можно считать рекурсивными. Такие справочники содержат реквизит, сгенерированный по этому же справочнику (Подразделения, Поручения, РКК, Договоры и т.д.).

Косвенно рекурсивным можно считать справочники Работников (через Подразделения: в Работниках есть реквизит Подразделение, в Подразделении указан Руководитель - работник).

Кроме того, рекурсивной можно считать таблицу задач. Косвенно рекурсивной таблицу заданий (через задачи).

Построим несколько запросов на примере справочника Подразделения. Стандартный справочник Подразделения в DIRECTUM можно считать рекурсивным, так как в нем есть реквизит, который ссылается на сам справочник Подразделения: Головное подразделение. Фактически, любой из примеров легко переделать для другого справочника или таблиц ЗЗУ.

Пример 1

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

-----------------------------------------------------------------
-- Иерархия подразделений
-----------------------------------------------------------------
with DipartmentHiererhcy (
  ID,
  HightID, 
  Level)
as
(
-- Подразделение верхнего уровня (N = 0)
  select
    department.Analit,
    department.Podr,
    0 as Level
  from
    MBAnalit department
  where
    department.Podr is null
    and department.vid = <ИД типа справочника Подразделения>
  union ALL
-- Подчиненное подразделение (N + 1)
  select
    department.Analit,
    department.Podr,
    Level + 1
  from
    MBAnalit as department
      inner join DipartmentHiererhcy as hightdepartment
        on department.Podr = hightdepartment.ID
  where
    department.vid = <ИД типа справочника Подразделения>
)
-- Иерархия подразделений
select
  hierarchy.ID as [ИД],
  department.NameAn as [Подразделение],
  IsNull(hightdepartment.NameAn, '') as [Ведущее подразделение],
  Level as [Уровень]
from
  DipartmentHiererhcy hierarchy
    left join MBAnalit department
      on department.Analit = hierarchy.ID
   left join MBAnalit hightdepartment
      on hightdepartment.Analit = hierarchy.HightID

Результат этого запроса на тестовой базе: список подразделений с указанием ведущего и уровня в иерархии.

Пример 2

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

-----------------------------------------------------------------
-- Иерархия подразделений вниз от указанного
-----------------------------------------------------------------
with DipartmentHiererhcy (
  ID,
  HightID, 
  Level)
as
(
-- Подразделение верхнего уровня (N = 0)
  select
    department.Analit,
    department.Podr,
    0 as Level
  from
    MBAnalit department
  where
    department.Analit = <ИД текущего подразделения>
    and department.vid = <ИД типа справочника Подразделения>
  union ALL
-- Подчиненное подразделение (N + 1)
  select
    department.Analit,
    department.Podr,
    Level + 1
  from
    MBAnalit as department
      inner join DipartmentHiererhcy as hightdepartment
        on department.Podr = hightdepartment.ID
  where
    department.vid = <ИД типа справочника Подразделения>
)
-- Иерархия подразделений вниз от указанного
select
  hierarchy.ID as [ИД],
  department.NameAn as [Подразделение],
  IsNull(hightdepartment.NameAn, '') as [Ведущее подразделение],
  Level as [Уровень]
from
  DipartmentHiererhcy hierarchy
    left join MBAnalit department
      on department.Analit = hierarchy.ID
   left join MBAnalit hightdepartment
      on hightdepartment.Analit = hierarchy.HightID

Пример 3

Также может возникнуть необходимость получить данные по всем подчиненным указанного работника:

-----------------------------------------------------------------
-- Список подчиненных указанного работника
-----------------------------------------------------------------
with DipartmentHiererhcy (
  ID,
  HightID, 
  Level)
as
(
-- Подразделение верхнего уровня (N = 0)
  select
    department.Analit,
    department.Podr,
    0 as Level
  from
    MBAnalit department
  where
    department.FIO = <ИД руководителя из справочника Работники>
    and department.vid = <ИД типа справочника Подразделения>
  union ALL
-- Подчиненное подразделение (N + 1)
  select
    department.Analit,
    department.Podr,
    Level + 1
  from
    MBAnalit as department
      inner join DipartmentHiererhcy as hightdepartment
        on department.Podr = hightdepartment.ID
  where
    department.vid = <ИД типа справочника Подразделения>
)
-- Список подчиненных указанного работника
select distinct
  department.NameAn as [Подразделение],
  employee.NameAn as [Работник],
  IsNull(chiefemployee.NameAn, '') as [Руководитель],
  Level as [Уровень]
from
  DipartmentHiererhcy hierarchy
    left join MBAnalit department
      on department.Analit = hierarchy.ID
   left join MBAnalit hightdepartment
      on hightdepartment.Analit = hierarchy.HightID
   left join MBAnalit employee
     on department.Analit = employee.Podr and employee.Vid = <ИД типа справочника Работники>
   left join MBAnalit chiefemployee
     on department.FIO = chiefemployee.Analit and chiefemployee.Vid = <ИД типа справочника Работники>
where
  employee.Analit is not null

Пример 4

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

-----------------------------------------------------------------
-- Список руководителей указанного работника
-----------------------------------------------------------------
with DipartmentHiererhcy (
  ID,
  HightID, 
  Level)
as
(
-- Подразделение нижнего уровня (N = 0)
  select
    department.Analit,
    department.Podr,
    0 as Level
  from
    MBAnalit department
  where
    department.Analit = <ИД текущего Работника>
    and department.vid = <ИД типа справочника Подразделения>
  union ALL
-- Ведущее подразделение (N + 1)
  select
    department.Analit,
    department.Podr,
    Level + 1
  from
    MBAnalit as department
      inner join DipartmentHiererhcy as hightdepartment
        on department.Analit = hightdepartment.HightID
  where
    department.vid = <ИД типа справочника Подразделения>
)
-- Список руководителей указанного работника
select distinct
  department.NameAn as [Подразделение],
  IsNull(chiefemployee.NameAn, '') as [Руководитель],
  Level as [Уровень]
from
  DipartmentHiererhcy hierarchy
    left join MBAnalit department
      on department.Analit = hierarchy.ID
    left join MBAnalit chiefemployee
      on department.FIO = chiefemployee.Analit and chiefemployee.Vid = <ИД типа справочника Работники>

В качестве P.S.

 Не стоит забывать о возможности существования бесконечной рекурсии. Во избежание используйте:

OPTION (MAXRECURSION <Максимальный уровень вложенности>)

 

26
Авторизуйтесь, чтобы оценить материал.
2
Андрей Воронин

Рекурсивные обобщенные табличные выражения, это хорошо, периодически тоже их использую, но вот только напрямую из директума они запускаются, нужно либо менять параметры соединения (возможно что-то сломается), либо выносить сам запрос в функцию или процедуру. Да и на больших объемах данных бывает, что работает не очень быстро (

За саму статью лайк!

Алексей Кузнецов

Подскажите, а как получить результат в виде дерева, чтобы в результате запроса получить примерно следующее: 

<dep level="0" name="подразденение0">
  <dep level = "1" name="подразденение1">
    <dep level="2" name="подразденение2"></dep>
	<dep level="2" name="подразденение3"></dep>
	<dep level="2" name="подразденение4"></dep>
	<dep level="2" name="подразденение5">
	  <dep level="3" name="подразденение6"></dep>
	  <dep level="3" name="подразденение7"></dep>
	  <dep level="3" name="подразденение8"></dep>
	  <dep level="3" name="подразденение9">
	     <dep level="4" name="подразденение10"></dep>
		 <dep level="4" name="подразденение11"></dep>
		 <dep level="4" name="подразденение12"></dep>
	  </dep>
	  <dep level="3" name="подразденение13"></dep>
	  <dep level="3" name="подразденение14">
	    <dep level="4" name="подразденение15"></dep>
		<dep level="4" name="подразденение16">
		  <dep level="5" name="подразденение17"></dep>
		  <dep level="5" name="подразденение18"></dep>
		</dep>
	  </dep>
	</dep>
  </dep>
</dep>
Алексей Кузнецов: обновлено 17.12.2018 в 08:09
Алексей Кузнецов: обновлено 17.12.2018 в 08:10
Алексей Кузнецов: обновлено 17.12.2018 в 08:11

Авторизуйтесь, чтобы написать комментарий