Решение без курсора (которое я лично предпочитаю):
WITH [CTE] AS
(
SELECT
T1.[Exec] AS [process],
1 AS [n],
T1.[Exec],
T1.[Exec] AS [parent]
FROM
[Table1] AS T1
UNION ALL
SELECT
C.[process],
C.[n] + 1,
T1.[Exec],
T2.[processid]
FROM
[CTE] AS C
INNER JOIN [Table1] AS T1 ON T1.[Exec] = C.[parent]
INNER JOIN [Table2] AS T2 ON T2.[stepid] = T1.[stepid]
)
SELECT C.[process], C.[parent]
FROM [CTE] AS C
WHERE C.[n] = (SELECT MAX([n]) FROM [CTE] WHERE [process] = C.[process])
Пояснение:
Якорная часть общего табличного выражения (запрос SELECT
перед UNION ALL
) определяет начальную точку операции. В этом случае он просто выбирает все данные из Table1
и имеет четыре поля:
process
будет содержать значение процесса (Exec
значение), родительский элемент которого должен быть определен.
n
будет содержать порядковый номер, начиная с 1.
Exec
будет содержать «смещающее» значение для объединения записей в следующей «рекурсивной» части общего табличного выражения.
parent
будет содержать соответствующее поле processid
из Table2
, которое представляет собой прямой родительский элемент значения Exec
.
Это выражение привязки даст следующие данные:
process n Exec parent
112 1 112 112
445 1 445 445
Рекурсивная часть общего табличного выражения (запрос SELECT
после UNION ALL
) продолжает добавлять записи в CTE из Table1
(где его значение Exec
равно значению parent
предыдущего CTE запись) и Table2
(связано с Table1
в полях stepid
). Эти новые записи в CTE будут иметь следующие значения полей:
process
будет скопировано из предыдущей записи CTE.
n
будет увеличено на 1.
Exec
получит значение Exec
объединенного значения Table1
Exec
(равное значению parent
предыдущей записи CTE).
parent
будет - снова - получит соответствующее значение processid
из Table2
, где его значение stepid
равно Table1
stepid
значению.
Весь CTE даст следующие результаты:
process n Exec parent
112 1 112 112
112 2 112 445
112 3 445 556
445 1 445 445
445 2 445 556
Основной запрос (ниже CTE) выберет только поля process
и parent
для каждой «последней» записи в CTE (где значение n
является наибольшим значением для этого конкретного process
значение, которое определяется с помощью подзапроса).
Это дает следующий конечный результат:
process parent
445 556
112 556
Надеюсь, это немного поможет.
Изменить относительно обновления в вопросе относительно 3-й таблицы Table0
:
Предполагая, что ваш запрос SELECT [process] FROM [Table0] WHERE [Name] LIKE '%Aswini%'
будет содержать допустимые процессы для возврата из вышеприведенного запроса, необходимо изменить только предложение WHERE основного запроса, приведенного выше.
Предыдущий WHERE-пункт:
WHERE C.[n] = (SELECT MAX([n]) FROM [CTE] WHERE [process] = C.[process])
Обновлено WHERE-предложение:
WHERE
C.[n] = (SELECT MAX([n]) FROM [CTE] WHERE [process] = C.[process]) AND
C.[process] IN (SELECT [process] FROM [Table0] WHERE [Name] LIKE '%Aswini%')
Изменить в отношении возможных дубликатов, когда процессы имеют более одного родителя
Если процесс имеет более одного родителя (??), приведенный выше запрос создает дубликаты. Чтобы устранить дубликаты и обеспечить более надежный способ определения самого верхнего родителя процесса, были внесены следующие изменения:
- Якорная часть CTE помещает фактического родителя процесса в поле
parent
, соединяя Table1
с Table2
. Это соединение должно быть левым, так что процессы без родителей (если возможно) также будут включены в результаты; их parent
значение будет равно их собственному идентификатору процесса.
- Рекурсивная часть CTE должна добавлять родителей только для процессов, у которых есть фактический родительский элемент (где поле
process
не равно parent
). Это позволяет избежать бесконечных циклов в рекурсивности (если это возможно).
- Основной запрос должен отфильтровывать все записи, в которых значение поля
parent
также используется в другой записи результата в качестве значения поля exec
для того же базового процесса (значение в поле process
). Потому что в этом случае поле parent
не является окончательным родительским значением, и эта другая запись результата может быть более подходящим кандидатом для содержания фактического родителя.
Другими словами: если у процесса A есть родительский B, а у процесса B родительский C, в CTE есть три связанных результата: (A, A, B), (A, B, C) и (B, B, C). ). Результат (A, A, B) недействителен, поскольку в результатах также доступен более подходящий кандидат (A, B, C). Окончательные результаты должны включать (A, C) и (B, C), но не (A, B).Эта логика реализована с использованием подзапроса в операторе EXISTS в предложении WHERE, но она также может быть реализована с использованием LEFT JOIN и на самом CTE.
- Из-за обновленной логики, описанной в пункте 3, столбец
n
CTE больше не используется и был удален.
- Чтобы избежать дублирования в случае «ромбовидной структуры» в данных (у процесса A есть родительские элементы B и C, а у обоих процессов B и C есть родительский D), DISTINCT используется в предложении SELECT основного запроса, чтобы избежать дублирования (A, D).
Окончательный запрос будет выглядеть так:
WITH [CTE] AS
(
SELECT
T1.[exec] AS [process],
T1.[exec],
COALESCE(T2.[processid], T1.[exec]) AS [parent]
FROM
[Table1] AS T1
LEFT JOIN [Table2] AS T2 ON T2.[stepid] = T1.[stepid]
UNION ALL
SELECT
C.[process],
T1.[exec],
T2.[processid]
FROM
[CTE] AS C
INNER JOIN [Table1] AS T1 ON T1.[exec] = C.[parent]
INNER JOIN [Table2] AS T2 ON T2.[stepid] = T1.[stepid]
WHERE
C.[parent] <> C.[process]
)
SELECT DISTINCT C.[process], C.[parent]
FROM [CTE] AS C
WHERE
NOT EXISTS (SELECT 1 FROM [CTE]
WHERE [process] = C.[process] AND [exec] = C.[parent])
AND C.[process] IN (SELECT [process] FROM [Table0] WHERE [name] LIKE '%Aswini%')
Я надеюсь, что это работает достаточно хорошо для вас.