没有光标的解决方案(我个人更喜欢):
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])
解释:
公用表表达式的锚点部分(UNION ALL 之前的SELECT 查询)定义了操作的起点。在这种情况下,它只是从Table1 中选择所有数据,它有四个字段:
-
process 将包含应确定其父进程的进程的值(Exec 值)。
-
n 将包含一个从 1 开始的序列号。
-
Exec 将包含一个“移位”值,用于在公用表表达式的下一个“递归”部分中连接记录。
-
parent 将包含来自 Table2 的相应 processid 字段,它表示 Exec 值的直接父级。
这个锚表达式将产生以下数据:
process n Exec parent
112 1 112 112
445 1 445 445
公用表表达式的递归部分(UNION ALL 之后的 SELECT 查询)不断将记录从 Table1 添加到 CTE(其中其 Exec 值等于前一个 CTE 的 parent 值记录)和Table2(与stepid字段上的Table1相关)。 CTE 中那些新添加的记录将具有以下字段值:
-
process 将从之前的 CTE 记录中复制。
-
n 将增加 1。
-
Exec 将获得加入的Table1 的Exec 值的Exec 值(等于先前CTE 记录的parent 值)。
-
parent 将 - 再次 - 从 Table2 获取相应的 processid 值,其中其 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 下方)将为 CTE 中的每个“最后”记录仅选择 process 和 parent 字段(其中 n 的值是该特定 process 的最大值值,使用子查询确定)。
这会产生以下最终结果:
process parent
445 556
112 556
希望这会有所帮助。
编辑关于第三张表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%')
当进程有多个父进程时,编辑可能的重复
如果一个进程有多个父进程 (??),上述查询会产生重复项。为了消除重复并提供一种更可靠的方法来确定进程的最顶层父级,进行了以下修改:
- 通过将
Table1 连接到Table2,CTE 的锚点部分将进程的实际父进程置于parent 字段中。这个连接应该是一个左连接,这样没有父进程(如果可能的话)也将包含在结果中;他们的parent 值将等于他们自己的进程ID。
- 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)。
此逻辑是使用 WHERE 子句中 EXISTS 运算符中的子查询来实现的,但当然也可以使用 CTE 本身的 LEFT JOIN 来实现。
- 由于第 3 点中描述的升级逻辑,CTE 的
n 列不再使用,已被删除。
- 为避免在数据中出现“菱形模式”的情况下出现重复(进程 A 有父进程 B 和 C,进程 B 和 C 都有父进程 D),在主查询的 SELECT 子句中使用 DISTINCT 以避免重复(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%')
我希望这对你来说足够好。