【问题标题】:SQL Server 2008 - Get the bottom most linked recordSQL Server 2008 - 获取最底部的链接记录
【发布时间】:2017-04-14 10:49:03
【问题描述】:

我的 SQL Server 数据库中有以下数据:

Id | Name | LinkedId
---+------+----------
1  | A    | 1
2  | B    | 2
3  | C    | 1
4  | D    | 3
5  | E    | 4

现在我想编写一个存储过程,其中应该显示以下记录:

注意LinkedId 具有与该名称相关联的 Id。

例如:“C”与“A”相关联

Id | Name | LinkedId
---+------+---------
1  | A    | 1
2  | B    | 2
3  | C    | 1
4  | D    | 1 //here instead of showing 3, it showed 1 which is the bottom most value in the tree
5  | E    | 1 //same case as the above

问题

对于这种情况,根据我有限的知识,我只能考虑使用 JOINS (LEFT, INNER),但在这种情况下,这不足以获得它的最底部链接 id。

编辑(输出):

我想要所有与项目“C”关联(直接和间接)的项目

Id | Name | 
---+------+
3  | C    | 
4  | D    | 
5  | E    | 

【问题讨论】:

  • 为什么要把最下面的两行更新为1,我不清楚。
  • @Kapil 它发生了变化,因为如果我们采用名称“D”的情况,它的 ID 是 4,它的链接 ID 是 3,如果你看到链接的 ID 3,它的子链接 ID 是 1如果您查看 ID 1,那么它就是 1(它不能进一步继续)。我需要得到它的最低价值。希望我说得通。

标签: sql sql-server sql-server-2008 stored-procedures logic


【解决方案1】:

你可以使用递归函数

一个简单的解释,使用 CTE 的递归函数是一个 common table expression,它在计算时使用它自己。它包括:

  1. 调用例程。

递归 CTE 的第一次调用 由一个或多个由 UNION ALL 连接的 CTE_query_definitions 组成, UNION、EXCEPT 或 INTERSECT 运算符。

因为这些查询定义 形成 CTE 结构的基本结果集,它们被称为 锚定成员。

CTE_query_definitions 被视为锚成员 除非他们引用 CTE 本身。

所有锚成员查询 定义必须位于第一个递归成员之前 定义,并且必须使用 UNION ALL 运算符来连接最后一个 锚成员与第一个递归成员。

  1. 例程的递归调用。

递归调用包括一个或多个 CTE_query_definitions 由引用 CTE 本身。这些查询定义称为递归 成员。

  1. 终止检查。

终止检查是隐式的; 当前一个没有返回行时递归停止 调用。

参考链接:Recursive query using CTE

Simple Example of Recursive CTE Cte sql server

DECLARE @SampleData AS TABLE (Id int, Name varchar(10), LinkedId int)

INSERT INTO @SampleData 
VALUES (1, 'A', 1), (2, 'B', 2), 
(3, 'C', 1),(4, 'D', 3),(5, 'A', 4)

;WITH temp AS 
(
   SELECT sd.Id, sd.Name,  sd.Id AS RootId 
   FROM @SampleData sd WHERE sd.LinkedId = sd.Id -- Invocation of the routine, in this case it's root node of tree.

   UNION ALL
   -- Recursive invocation of the routine
   SELECT sd.Id, sd.Name,  t.RootId AS RootId  
   FROM temp t
   INNER JOIN @SampleData sd ON sd.LinkedId = t.Id AND sd.LinkedId <> sd.Id
   -- Termination check: sd.LinkedId = t.Id AND sd.LinkedId <> sd.Id. 
   -- It make recursive query is not an infinitive loop
)
SELECT  t.Id, t.Name, t.RootId AS LinkedId
FROM temp t
OPTION (MAXRECURSION 0) -- this option remove recursive max depth, default is 100.

演示链接:Rextester

对于新的输出,您可以更改The first invocation of the recursive CTE

;WITH temp AS 
(
   SELECT sd.Id, sd.Name,  sd.Id AS RootId 
   FROM @SampleData sd WHERE sd.Id = 3

   UNION ALL
   -- Recursive invocation of the routine
   SELECT sd.Id, sd.Name,  t.RootId AS RootId  
   FROM temp t
   INNER JOIN @SampleData sd ON sd.LinkedId = t.Id AND sd.LinkedId <> sd.Id
   -- Termination check: sd.LinkedId = t.Id AND sd.LinkedId <> sd.Id. 
   -- It make recursive query is not an infinitive loop
)
SELECT  t.Id, t.Name, t.RootId AS LinkedId
FROM temp t
OPTION (MAXRECURSION 0) -- this option remove recursive max depth, default is 100.

【讨论】:

  • 感谢您的回答,但您能详细解释一下这是如何工作的吗?
  • 感谢您的编辑,很抱歉问得越来越多,但您能否编辑解决方案以获取项目“C”下的所有相关项目,这些项目应该是项目“C”, D”和“E”
  • 请检查更新后的输出............请帮我获得新的输出,但请确保项目“A”没有进入输出列表,因为我想要“C”下的所有项目。
  • 对不起,我的朋友,但我试过了,似乎“存在(从 @SampleData sd2 选择 1,其中 sd2.Id = sd2.LinkedId 和 sd2.Id = sd.LinkedId)”是没用的,因为无论值我将“1”更改为答案保持不变。
  • 条件 EXISTS (SELECT 1 FROM @SampleData sd2 WHERE sd2.Id = sd2.LinkedId AND sd2.Id = sd.LinkedId) 检查此节点是根节点的直接子节点。我尝试我的查询,它会返回您想要的结果。
【解决方案2】:

你想要一个递归过程:

with cte as (
      select t.id, t.name, t.linkedid, 1 as lev
      from t
      where t.linkedid = t.id
      union all
      select t.id, t.name, cte.linkedid, cte.lev + 1
      from t join
           cte
           on cte.id = t.linkedid and t.linkedid <> t.id
     )
select id, name, linkedid
from cte;

Here 在实践中是否有效。

【讨论】:

  • 感谢您尝试 mate,但它不起作用。它给了我错误消息“无法绑定多部分标识符“t.linkedid”。”是的,我用真实的字段名称更改了“linkedid”。
  • @Agent_Spock 。 . .没有基于列名称的错误消息。显示了一个工作版本。原始版本确实进入了无限循环,因为它没有过滤掉递归部分中的根音符(因此出现了t.linkedid &lt;&gt; t.id 条件)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-30
  • 1970-01-01
相关资源
最近更新 更多