家族树的递归CTE不会递归

时间:2017-04-10 13:19:19

标签: sql sqlite recursion common-table-expression

我正在尝试使用我有限的SQL技能来构建递归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,数据库浏览器,在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查询中会导致我出现问题。到目前为止,我的尝试导致了这个查询的怪物,但它只返回一条记录(&#34; 1&#34;&#34;威廉一世&#34;&#34; 2&#34;&#34; Matilde of法兰德斯&#34;&#34; NULL&#34;&#34; NULL&#34;&#34; NULL&#34;&#34; NULL&#34;),显然它没有进入递归

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树中使用它。

1 个答案:

答案 0 :(得分:1)

很酷,我想我明白了。线索是在它确实返回的那条记录中,我忽略了它。该记录没有parentId,因为它是root用户。但在查询中,我将下一个Person的'bornToId'与此parentId匹配,值为NULL。

我应该匹配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