当表具有指向自身的链接时的 SQL 查询

Posted

技术标签:

【中文标题】当表具有指向自身的链接时的 SQL 查询【英文标题】:SQL query when a table has a link to itself 【发布时间】:2012-06-02 10:16:27 【问题描述】:

你好,说我有一张桌子:

Person:
PersonId
Name
ManagerId

所以 ManagerId 是对另一个人的引用。

所以数据库中可能有一个人:

1
Bob
null

2
Steve
1

3
Tim
2

所以 Bob 是 Steve 的经理,Steve 是 Tim 的经理。

所以我想做的是编写一个查询,让 Bob 管理下的所有人。无论是直接的还是间接的。所以我想同时得到史蒂夫和蒂姆。在同一行。

如果我写:

select * from Person
where ManagerId = 1 I would get only Steve.

我该如何写才能让每个人都直接或间接地服从 Bob?

【问题讨论】:

什么数据库?不同的数据库对此任务使用不同的方法。 这是树形结构吗?就像 a)Bob 是 Steve 和 George 的经理(示例)。Steve 是 Anne 和 Nick 的经理。George 是 Stella 和 Bruce 的经理。乔治与安妮和尼克无关。史蒂夫与斯特拉和布鲁斯毫无关系。或者它是一个简单的链? Bob->Steve->Tim->Nick->Stella->等 【参考方案1】:

您可以使用公用表表达式 (CTE) 来解决此问题。正如 Andrei 指出的那样,CTE 可用于递归(请参阅 Andrei 在他的帖子中包含的优秀参考资料)。假设您有一个如下表:

create table Person
(
   PersonId int primary key,
   Name varchar(25),
   ManagerId int foreign Key references Person(PersonId)
)

让我们将以下数据插入到表中:

insert into Person (PersonId, Name, ManagerId) values 
    (1,'Bob', null),
    (2, 'Steve',1),
    (3, 'Tim', 2)
    (4, 'John', 3),
    (5, 'James', null),
    (6, 'Joe', 5)

然后我们想要一个查询,该查询将返回直接或间接向 Bob 报告的每个人,即 Steve、Tim 和 John。我们不想返回 James 和 Bob,因为他们没有向任何人报告,或者 Joe,因为他向 James 报告。这可以通过 CTE 查询来完成,如下所示:

WITH Managers AS 
( 
     --initialize
     SELECT PersonId, Name, ManagerId  
        FROM Person WHERE ManagerId =1
     UNION ALL 
     --recursion 
     SELECT p.PersonId, p.Name, p.ManagerId 
        FROM Person p INNER JOIN Managers m  
        ON p.ManagerId = m.PersonId 
) 
SELECT * FROM Managers

此查询返回正确的结果:

PersonId    Name                      ManagerId
----------- ------------------------- -----------
2           Steve                     1
3           Tim                       2
4           John                      3

编辑:假设 OP 使用 SQL Server 2005 或更高版本,此答案是有效的。我不知道这种语法在 mysql 或 Oracle 中是否有效。

【讨论】:

【参考方案2】:

如果您使用的是 MS SQL Server 2005 或更高版本,则可以使用 CTE,正如 @AndreiDrynov 指出的那样,如下所示:

;WITH Emps(PersonId, Name, PersonLevel, ManagerName)
AS
(
    SELECT PersonId, Name, 0 AS PersonLevel, 
           CONVERT(NVARCHAR(50), 'No Manager') AS ManagerName
    FROM Persons
    WHERE ManagerId IS NULL
    Union All
    SELECT p.PersonId, P.Name, e.PersonLevel + 1 , e.Name 
    FROM Persons p 
    INNER JOIN Emps e ON p.ManagerId = e.PersonId 
 ) 
SELECT * 
FROM Emps 
WHERE PersonLevel <= 2

此查询应为您提供以下信息:

  PersonId   |   Name  |  Peroson Level   |   Manager
 ------------+---------+------------------+--------------
     1           Bob           0             No Manager
     2          Steve          1                Bob
     3           Tim           2               Steve

您可以在这里看到它的实际效果:

DEMO

【讨论】:

【参考方案3】:
select * 
from Person A 
inner join Person B ON B.PersonID = A.ManagerID
where ManagerId = 1

【讨论】:

以上是关于当表具有指向自身的链接时的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

sql语句中怎么把查询出来的字段数据当表名再进行查询?

SQL:将列值链接到列名以满足某些条件

sql链接查询

当表名是参数时Oracle使用动态sql

sql查询语句心得

具有多个左外连接的 SQL Server 查询挂起