2017-04-10 121 views
2

我正在尝试使用我有限的SQL技能来构造递归CTE查询。 我有一个建模的家谱两个表:家族树的递归CTE不会递归

CREATE TABLE "Relation" (
    "id" integer not null primary key autoincrement, 
    "person1Id" integer, 
    "person2Id" integer, 
    foreign key("person1Id") references "Person"("id"), 
    foreign key("person2Id") references "Person"("id")); 

CREATE TABLE "Person" (
    "id" integer not null primary key autoincrement, 
    "name" varchar(255), 
    "gender" varchar(255), 
    "bornToId" integer, 
    foreign key("bornToId") references "Relation"("id")); 

我试图让一个sqlfiddle,但我不断收到“哎呀,出事了”,所以我会在这里粘贴插入:

INSERT INTO `Person` (id,name,gender,bornToId) VALUES 
(1,'William I','M',NULL), 
(2,'Matilde of Flanders','F',NULL), 
(3,'William Rufus','M',1), 
(4,'Henry I','M',1), 
(5,'Matilde of Scotland','F',NULL), 
(6,'William Adelin','M',2), 
(7,'Matilde Holy Roman Empress','F',2), 
(8,'Geoffrey of Anjou','M',NULL), 
(9,'Henry II','M',3), 
(10,'Eleanor of Aquitane','F',NULL), 
(11,'Richard I','M',4), 
(12,'John','M',4), 
(13,'Robert Curthose','M',1), 
(14,'Adeliza','F',NULL), 
(15,'Adela of Normandy','F',1), 
(16,'Stephen II Henry','M',NULL), 
(17,'Stephen of Blois','M',6); 

INSERT INTO `Relation` (id,person1Id,person2Id) VALUES (1,1,2), 
(2,4,5), 
(3,7,8), 
(4,9,10), 
(5,4,14), 
(6,15,16); 

我开始将问题分解为子查询。这些,因为我希望他们(sqlite的3.14.0,DB浏览器,在Mac上)所有的工作:

找到0,1或多种关系(合作伙伴)的人

select 
    pers.id as id, 
    pers.name as name, 
    partner.id as partnerId, 
    partner.name as partnerName 
from Person pers 
join Relation 
    on pers.id = Relation.person1Id 
    or pers.id = Relation.person2Id 
join Person partner 
    on (partner.id = Relation.person1Id and 
    partner.id <> pers.id) 
    or (partner.id = Relation.person2Id and 
    partner.id <> pers.id) 
where pers.id = 4 

发现一个人的父母(如果有的话)

select 
    parent1.id as parent1Id, 
    parent1.name as parent1Name, 
    parent2.id as parent2Id, 
    parent2.name as parent2Name, 
    parents.id as parentsId 
from Person pers 
left join Relation parents on pers.bornToId = parents.id 
left join Person parent1 on parents.person1Id = parent1.id 
left join Person parent2 on parents.person2Id = parent2.id 
where pers.id = 4 

结合上述两个也可以,但是我会在这里忽略该查询。

找到属于关系

select pers.id, pers.name 
from Person pers 
where pers.bornToId = 1 

现在的孩子,把这个都聚集在递归CTE查询造成我的问题。我迄今为止的尝试导致了这个查询的怪物,但它只是返回一个记录(“1”“威廉一世”“2”“佛罗里达的Matilde”“NULL”“NULL”“NULL”“NULL”),所以显然它不会进入递归。

WITH FamilyTree (
    id, 
    name, 
    partnerId, 
    partnerName, 
    parent1Id, 
    parent1Name, 
    parent2Id, 
    parent2Name, 
    parentsId 
) 
AS (
select 
    pers.id as id, 
    pers.name as name, 
    partner.id as partnerId, 
    partner.name as partnerName, 
    parent1.id as parent1Id, 
    parent1.name as parent1Name, 
    parent2.id as parent2Id, 
    parent2.name as parent2Name, 
    parents.id as parentsId 
from Person pers 
left join Relation 
    on pers.id = Relation.person1Id 
    or pers.id = Relation.person2Id 
left join Person partner 
    on (partner.id = Relation.person1Id and 
     partner.id <> pers.id) 
    or (partner.id = Relation.person2Id and 
     partner.id <> pers.id) 
left join Relation parents on pers.bornToId = parents.id 
left join Person parent1 on parents.person1Id = parent1.id 
left join Person parent2 on parents.person2Id = parent2.id 
where pers.id = 1 
union all 


select 
    pers.id as id, 
    pers.name as name, 
    partner.id as partnerId, 
    partner.name as partnerName, 
    parent1.id as parent1Id, 
    parent1.name as parent1Name, 
    parent2.id as parent2Id, 
    parent2.name as parent2Name, 
    parents.id as parentsId 
from Person pers 
left join Relation 
    on pers.id = Relation.person1Id 
    or pers.id = Relation.person2Id 
left join Person partner 
    on (partner.id = Relation.person1Id and 
     partner.id <> pers.id) 
    or (partner.id = Relation.person2Id and 
     partner.id <> pers.id) 
left join Relation parents on pers.bornToId = parents.id 
left join Person parent1 on parents.person1Id = parent1.id 
left join Person parent2 on parents.person2Id = parent2.id 

JOIN FamilyTree AS fam 
    ON pers.bornToId = fam.parentsId 
) 


select 
    id, 
    name, 
    partnerId, 
    partnerName, 
    parent1Id, 
    parent1Name, 
    parent2Id, 
    parent2Name 
from FamilyTree 

我在这方面花了很多时间,所以我认为是时候问专家了。

如果你想知道,最终目标是到嵌套JavaScript对象,如:

{ 
    ... 
    relations: [{ 
    ... 
    children: [{ 

这样我就可以在D3树使用它。

+0

在CTE末尾添加“LIMIT 20”。结果是什么? –

+0

我在正式结束之前就放下了)。我再次得到“查询成功执行...花了5毫秒”。 – devboell

+0

哦,等等,它返回的东西(有和没有LIMIT),一个记录: “1” \t “威廉一世” \t “2” \t “弗兰德的马蒂尔德” \t “NULL” \t “NULL” \t “NULL” \t “NULL”我认为这仍然是前一个查询的结果。我将编辑该问题 – devboell

回答

1

很酷,我想我明白了。这个线索就是我们忽略的一条记录。该记录没有parentId,因为它是根人。但在查询中,我将下一个Person的'bornToId'与值为NULL的parentId进行匹配。

我应该根据root用户的关系的id进行匹配,我没有在select中定义它。

在这个修改后的cte查询中,它确实进入递归,结果看起来不错。我仍然需要做一些检查,但是我会张贴这个来保存其他人找到这个特定错误的麻烦。

WITH FamilyTree (
    id, 
    name, 
    partnerId, 
    partnerName, 
    parent1Id, 
    parent1Name, 
    parent2Id, 
    parent2Name, 
    parentsId, 
    relationId 
) 
AS (
select 
    pers.id as id, 
    pers.name as name, 
    partner.id as partnerId, 
    partner.name as partnerName, 
    parent1.id as parent1Id, 
    parent1.name as parent1Name, 
    parent2.id as parent2Id, 
    parent2.name as parent2Name, 
    parents.id as parentsId, 
    rel.id as relationId 
from Person pers 
left join Relation rel 
    on pers.id = rel.person1Id 
    or pers.id = rel.person2Id 
left join Person partner 
    on (partner.id = rel.person1Id and 
     partner.id <> pers.id) 
    or (partner.id = rel.person2Id and 
     partner.id <> pers.id) 
left join Relation parents on pers.bornToId = parents.id 
left join Person parent1 on parents.person1Id = parent1.id 
left join Person parent2 on parents.person2Id = parent2.id 
where pers.id = 1 
union all 


select 
    pers.id as id, 
    pers.name as name, 
    partner.id as partnerId, 
    partner.name as partnerName, 
    parent1.id as parent1Id, 
    parent1.name as parent1Name, 
    parent2.id as parent2Id, 
    parent2.name as parent2Name, 
    parents.id as parentsId, 
    rel.id as relationId 
from Person pers 
left join Relation rel 
    on pers.id = rel.person1Id 
    or pers.id = rel.person2Id 
left join Person partner 
    on (partner.id = rel.person1Id and 
     partner.id <> pers.id) 
    or (partner.id = rel.person2Id and 
     partner.id <> pers.id) 
left join Relation parents on pers.bornToId = parents.id 
left join Person parent1 on parents.person1Id = parent1.id 
left join Person parent2 on parents.person2Id = parent2.id 

JOIN FamilyTree AS fam 
    ON pers.bornToId = fam.relationId 
) 


select 
    id, 
    name, 
    partnerId, 
    partnerName, 
    parent1Id, 
    parent1Name, 
    parent2Id, 
    parent2Name 
from FamilyTree