【问题标题】:How to write a recursive query with 2 tables in SQL Server如何在 SQL Server 中使用 2 个表编写递归查询
【发布时间】:2019-04-08 08:41:34
【问题描述】:

我有一个具有以下结构的表。 表名:Table0

结构如下

Select process from Table0 where Name like '%Aswini%'

Process
-------
112
778
756

所有这些过程必须进入下表 表名:Table1

结构如下

Select Exec, stepid, condition 
from Table1 
where Exec = 112

Exec   stepid condition
-----------------------
112     2233     0
112     2354     0
445     3455     0

第二个表'Table 2'结构如下:

Select stepid, processid  
from Table2 
where stepid = 2233

Stepid processid
-----------------
2233      445
2354      566
3455      556

Table1 stepid 是 Table2 stepid 的输入,Table2 Processid 是 Table1 Exec 的输入。我必须递归获取processID,直到条件为 0,否则表不返回任何行,最终 processid 是父 ID。

我没有从事 CTE 工作。所以我使用了一个简单的连接来得到以下结果。

select b.processid 
from Table1 a 
inner join Table2 b on a.stepid = b.stepid 
where a.condition = 0 
  and a.exec = 112(parent from table0)

如果满足条件,上述查询将给我 Exec 112 的父级。

我必须再次输入父查询并执行它。

我可以在 C# 的帮助下通过循环来实现这一点。但我只想在 SQL Server 中使用它。这可以实现吗?

编辑

当我执行 CTE 时,我得到以下结果

Process Parent
  112     445
  112     566
  112     445
  112     566

如果初始进程有 2 个 exec,则最终进程的父结构复制两次(exec 数)。为什么会这样。它必须只显示一次结果。

【问题讨论】:

  • 可以通过在Sql-server中创建一个函数来实现。

标签: c# sql-server common-table-expression recursive-query dynamicquery


【解决方案1】:

没有光标的解决方案(我个人更喜欢):

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 将获得加入的Table1Exec 值的Exec 值(等于先前CTE 记录的parent 值)。
  • parent 将 - 再次 - 从 Table2 获取相应的 processid 值,其中其 stepid 值等于 Table1stepid 值。

整个 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 中的每个“最后”记录仅选择 processparent 字段(其中 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%')

当进程有多个父进程时,编辑可能的重复

如果一个进程有多个父进程 (??),上述查询会产生重复项。为了消除重复并提供一种更可靠的方法来确定进程的最顶层父级,进行了以下修改:

  1. 通过将Table1 连接到Table2,CTE 的锚点部分将进程的实际父进程置于parent 字段中。这个连接应该是一个左连接,这样没有父进程(如果可能的话)也将包含在结果中;他们的parent 值将等于他们自己的进程ID。
  2. CTE 的递归部分应该只为具有实际父进程的进程添加父进程(其中字段process 不等于parent)。这是为了避免递归中的无限循环(如果可能)。
  3. 主查询应过滤掉所有记录,其中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 来实现。
  4. 由于第 3 点中描述的升级逻辑,CTE 的 n 列不再使用,已被删除。
  5. 为避免在数据中出现“菱形模式”的情况下出现重复(进程 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%')

我希望这对你来说足够好。

【讨论】:

  • 如果您提供有关代码如何工作的解释,这将是一个更好的答案。
  • 好的。我将添加一些解释。
  • 哇。它按我的意愿工作。我有其他用例。如果 Exec 值来自不同的表怎么办。 CTE 是否适用于 3 个 tables.ex 查询:从表 0 中选择进程,其中名称类似于 '%Aswini%'
  • 我已经编辑了我的问题。你能告诉我是否可以进行 3 表递归吗?
  • 好的。我查看了更新的问题。看起来 Table1 和 Table2 之间的关系保持不变,因此查询可能不需要结构更新。 Table0 似乎是一个包含“Aswini 进程”的新表。查询现在应该只显示 Table1 中也包含在 Table0 中的进程是否正确?
【解决方案2】:

您是否尝试使用游标并将第一个表存储在游标中,并通过直到游标末尾获取进程ID。

DECLARE f_cursor CURSOR FOR     
Select Exec, stepid
from Table1

OPEN f_cursor    

FETCH NEXT FROM f_cursor     
INTO @exec,@stepid
WHILE @@FETCH_STATUS = 0    
BEGIN
select b.processid 
from Table1 a 
inner join Table2 b on a.stepid = b.stepid 
where a.condition = 0 
  and a.exec =@exec
//store the processid somewhere for later use.
END     
CLOSE f_cursor;    
DEALLOCATE f_cursor;

【讨论】:

  • 我得到一个错误,必须在这一步声明一个标量变量“@exec”'和a.exec =@exec'
  • 下面是我的查询 DECLARE f_cursor CURSOR FOR Select Process from table0 OPEN f_cursor FETCH NEXT FROM f_cursor INTO @Process WHILE @@FETCH_STATUS = 0 BEGIN select b.processid from Table1 a inner join Table2 b on a。 StepID = b.StepID 其中 a.Condition = 0 和 a.exec =@ProcessID END CLOSE f_cursor; DEALLOCATE f_cursor;
  • 然后用exec和stepid声明一个变量
猜你喜欢
  • 2012-10-21
  • 1970-01-01
  • 2014-12-19
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多