Oracle SYS_CONNECT_BY_PATH эквивалентный запрос в SQL Server

Я пытаюсь преобразовать сложный запрос с использованием синтаксиса Oracle SYS_CONNECT_BY_PATH в SQL Server:

    SELECT 
           DISTINCT TO_CHAR(CONCAT(@ROOT, SYS_CONNECT_BY_PATH(CONCAT('C_',X), '.'))) AS X_ALIAS
                , TO_CHAR(CONCAT(@ROOT, PRIOR SYS_CONNECT_BY_PATH(CONCAT('C_',X), '.'))) AS X_ALIAS_FATHER
                , TO_CHAR(X) AS X_ALIAS_LEAF
                , LEVEL AS LVL
      FROM MY_TABLE
 LEFT JOIN MY_TABLE_BIS MY_TABLE_BIS_ALIAS ON MY_TABLE_BIS_ALIAS.MY_ID = COL_X
 LEFT JOIN OTHER_TABLE 
        ON OTHER_TABLE.MY_ID = COL_X
CONNECT BY (PRIOR ID_SON = ID_FATHER)
       AND LEVEL <= MAXDEPTH
START WITH ID_FATHER 
        IN (SELECT AN_ID AS ID_FATHER FROM BIG_TABLE)

Вот что я получил, используя этот сайт

WITH n(LEVEL, X_ALIAS, X_ALIAS_FATHER, X_ALIAS_LEAF) AS
      ( SELECT 1, CONCAT('C_',X), CONCAT('C_',X), CAST(X AS VARCHAR(30))
          FROM MY_TABLE
     LEFT JOIN MY_TABLE_BIS MY_TABLE_BIS_ALIAS 
            ON MY_TABLE_BIS_ALIAS.MY_ID = COL_X
     LEFT JOIN OTHER_TABLE 
            ON OTHER_TABLE.MY_ID = COL_X
         WHERE ID_FATHER IN (SELECT AN_ID AS ID_FATHER 
          FROM listAllCfaCfq)
     UNION ALL
        SELECT n.level + 1, n.X_ALIAS + '.' + nplus1.X_ALIAS, n.X_ALIAS_FATHER + '.' + nplus1.X_ALIAS_FATHER, CAST(X AS VARCHAR(30)
          FROM MY_TABLE
     LEFT JOIN MY_TABLE_BIS MY_TABLE_BIS_ALIAS 
            ON MY_TABLE_BIS_ALIAS.MY_ID = COL_X
     LEFT JOIN OTHER_TABLE 
            ON OTHER_TABLE.MY_ID = COL_X AS nplus1, n
         WHERE n.ID_SON = nplus1.ID_FATHER)
        SELECT DISTINCT LEVEL, X_ALIAS, X_ALIAS_FATHER, X_ALIAS_LEAF
         WHERE LEVEL <= @MAXDEPTH;

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

Ответы

Ответ 1

Все можно решить, используя несколько функций, и это просто с некоторой рекурсией (обратите внимание, что максимальный уровень рекурсии в T-SQL равен 32)

Предполагая, что у нас есть следующая таблица: (для очень маленькой компании)

TableName: Employees
id.....name..............manager_id
1      Big Boss          NULL
2      Sales Manager     1
3      Support Manager   1
4      R&D Manager       1               
5      Sales man         2
6      Support man       3
7      R&D Team leader   4
8      QA Team leader    4
9      C Developer       7
10     QA man            8
11     Java Developer    7      

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

Первая функция очень проста с использованием рекурсии:

Create Function dbo.Do_WE_Have_path(@id int, @boss_id int, @max_level int) returns int
Begin
  declare @res int, @man int
  set @res = 0
  if @id = @boss_id 
    set @res = 1
  else if @max_level > 0 
  Begin
    Select @man=manager_id from Employees where [email protected]
    set @res = Do_WE_Have_path(@man, @boss_id, @max_level-1) --recursion
  End
  return res 
End

Используя вышеупомянутую функцию, мы можем выбрать все объекты с соединением, которое короче или равно указанному уровню, поэтому теперь мы можем написать метод, который строит путь, если он существует, обратите внимание, что несуществующий путь должен быть отфильтрован с использованием вышеуказанного метода.

Create Function dbo.Make_The_path(@id int, @boss_id int, @max_level int) returns varchar(max)
Begin
  declare @res varchar(max), @man int
  select @res = name from Employees where [email protected]
  if max_level > 0 AND @id <> @boss_id
  Begin 
    select @man = manager_id from Employees where id = @id
    set @res = dbo.Make_The_path(@man, @boss_id, max_level-1) + '/' + @res
  End
  return @res
End

Теперь мы можем использовать обе функции, чтобы получить путь от начальника к рабочим:

Select dbo.Make_The_path(id, 1, 3) Where Do_WE_Have_path(id, 1, 3)=1    

Обе функции могут быть объединены в одну, и, возможно, вам придется написать ее снова для каждой структуры, но важно то, что это возможно.