问题描述
假设我有下表:
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
我想在一个查询中检索詹姆斯的所有后代(珍娜,珍妮弗,彼得,爱丽丝,史蒂夫). 谢谢, 帕勃罗.
推荐答案
在SQL Server 2005上,您可以使用 a>:
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
其他推荐答案
为了自下而上,请使用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
其他推荐答案
如果没有存储过程,您将无法在SQL中进行递归.解决此问题的方法是使用嵌套集,它们基本上将SQL中的一棵树建模为一组.
请注意,这将需要更改当前数据模型或可能弄清楚如何在原始模型上创建视图.
postgresql示例(使用极少数PostgreSQL扩展,仅序列上和提交下降,大多数RDBMSS都具有相似的功能):
设置:
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;
问题描述
Let's say I have the following table:
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
I want to retrieve in one query all the descendants of James (Jenna,Jennifer,Peter, Alice, Steve). Thanks, Pablo.
推荐答案
On SQL Server 2005 you can use 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
其他推荐答案
For bottom up use mathieu's answer with a little modification:
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
其他推荐答案
You can't do recursion in SQL without stored procedures. The way to solve this is using Nested Sets, they basically model a tree in SQL as a set.
Notice that this will require a change to the current data model or possibly figuring out how to create a view on the original model.
Postgresql example (using very few postgresql extensions, just SERIAL and ON COMMIT DROP, most RDBMSes will have similar functionality):
Setup:
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);
Adding a child:
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;
Display a trail from bottom to top:
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;
Display the entire tree:
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 everything down from a certain element of the tree:
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;