如何在 MSSQL 2005 中创建递归查询?
Posted
技术标签:
【中文标题】如何在 MSSQL 2005 中创建递归查询?【英文标题】:How do I create a recursive query in MSSQL 2005? 【发布时间】:2010-09-19 08:25:10 【问题描述】:假设我有下表:
CustomerID ParentID Name
========== ======== ====
1 null John
2 1 James
3 2 Jenna
4 3 Jennifer
5 3 Peter
6 5 Alice
7 5 Steve
8 1 Larry
我想在一个查询中检索 James 的所有后代(Jenna、Jennifer、Peter、Alice、Steve)。 谢谢, 巴勃罗。
【问题讨论】:
解决方案应该在什么 RDBMS 中运行?如果是 Oracle,请了解 CONNECT BY PRIOR 对不起,忘了说,在 MSSQL 2005 中 【参考方案1】:在 SQL Server 2005 上,您可以使用 CTEs (Common Table Expressions):
with Hierachy(CustomerID, ParentID, Name, Level)
as
(
select CustomerID, ParentID, Name, 0 as Level
from Customers c
where c.CustomerID = 2 -- insert parameter here
union all
select c.CustomerID, c.ParentID, c.Name, ch.Level + 1
from Customers c
inner join Hierachy ch
on c.ParentId = ch.CustomerID
)
select CustomerID, ParentID, Name
from Hierachy
where Level > 0
【讨论】:
如果您不想使用递归或者您需要从子代获取父代,您可以从 Rob Volk 的 sqlteam 文章中实现解决方案:sqlteam.com/article/more-trees-hierarchies-in-sql【参考方案2】:对于自下而上使用 mathieu 的答案并稍作修改:
with Hierachy(CustomerID, ParentID, Name, Level)
as
(
select CustomerID, ParentID, Name, 0 as Level
from Customers c
where c.CustomerID = 2 -- insert parameter here
union all
select c.CustomerID, c.ParentID, c.Name, ch.Level + 1
from Customers c
inner join Hierachy ch
-- EDITED HERE --
on ch.ParentId = c.CustomerID
-----------------
)
select CustomerID, ParentID, Name
from Hierachy
where Level > 0
【讨论】:
【参考方案3】:没有存储过程就不能在 SQL 中进行递归。解决这个问题的方法是使用嵌套集,它们基本上将 SQL 中的树建模为一个集合。
请注意,这将需要对当前数据模型进行更改,或者可能需要弄清楚如何在原始模型上创建视图。
Postgresql 示例(使用很少的 postgresql 扩展,仅使用 SERIAL 和 ON COMMIT DROP,大多数 RDBMS 将具有类似的功能):
设置:
CREATE TABLE objects(
id SERIAL PRIMARY KEY,
name TEXT,
lft INT,
rgt INT
);
INSERT INTO objects(name, lft, rgt) VALUES('The root of the tree', 1, 2);
添加一个孩子:
START TRANSACTION;
-- postgresql doesn't support variables so we create a temporary table that
-- gets deleted after the transaction has finished.
CREATE TEMP TABLE left_tmp(
lft INT
) ON COMMIT DROP; -- not standard sql
-- store the left of the parent for later use
INSERT INTO left_tmp (lft) VALUES((SELECT lft FROM objects WHERE name = 'The parent of the newly inserted node'));
-- move all the children already in the set to the right
-- to make room for the new child
UPDATE objects SET rgt = rgt + 2 WHERE rgt > (SELECT lft FROM left_tmp LIMIT 1);
UPDATE objects SET lft = lft + 2 WHERE lft > (SELECT lft FROM left_tmp LIMIT 1);
-- insert the new child
INSERT INTO objects(name, lft, rgt) VALUES(
'The name of the newly inserted node',
(SELECT lft + 1 FROM left_tmp LIMIT 1),
(SELECT lft + 2 FROM left_tmp LIMIT 1)
);
COMMIT;
从下到上显示轨迹:
SELECT
parent.id, parent.lft
FROM
objects AS current_node
INNER JOIN
objects AS parent
ON
current_node.lft BETWEEN parent.lft AND parent.rgt
WHERE
current_node.name = 'The name of the deepest child'
ORDER BY
parent.lft;
显示整棵树:
SELECT
REPEAT(' ', CAST((COUNT(parent.id) - 1) AS INT)) || '- ' || current_node.name AS indented_name
FROM
objects current_node
INNER JOIN
objects parent
ON
current_node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY
current_node.name,
current_node.lft
ORDER BY
current_node.lft;
从树的某个元素向下选择所有内容:
SELECT
current_node.name AS node_name
FROM
objects current_node
INNER JOIN
objects parent
ON
current_node.lft BETWEEN parent.lft AND parent.rgt
AND
parent.name = 'child'
GROUP BY
current_node.name,
current_node.lft
ORDER BY
current_node.lft;
【讨论】:
但是你可以,看马修的回答。 我发布的代码使用了适用于任何 ANSI-SQL 数据库的技术,因为 OP 忘记提及他在原始帖子中使用的 RDBMS(请参阅该帖子的 cmets)。【参考方案4】:除非我遗漏了什么,否则递归是不必要的......
SELECT d.NAME FROM Customers As d
INNER JOIN Customers As p ON p.CustomerID = d.ParentID
WHERE p.Name = 'James'
【讨论】:
这样你不会得到詹妮弗、彼得、爱丽丝、史蒂夫以上是关于如何在 MSSQL 2005 中创建递归查询?的主要内容,如果未能解决你的问题,请参考以下文章