如何实现广泛的多级自连接PostgreSQL查询?

问题描述 投票:0回答:1

我有几行代表

person
。每个
person
都有一个
bio_mother _uuid
列和一个
bio_father_uuid
。我使用此页面(我提到这一点是因为它在这个类似的问题中被引用)来实现此连接。

SELECT 
base.uuid base_uuid, base.created_at base_created_at, base.givenname base_givenname, base.bio_mother_uuid base_bio_mother_uuid, base.bio_father_uuid base_bio_father_uuid,
mother.uuid mother_uuid, mother.created_at mother_created_at, mother.givenname mother_givenname, mother.bio_mother_uuid mother_bio_mother_uuid, mother.bio_father_uuid mother_bio_father_uuid,
father.uuid father_uuid, father.created_at father_created_at, father.givenname father_givenname, father.bio_mother_uuid father_bio_mother_uuid, father.bio_father_uuid father_bio_father_uuid
FROM person AS base
LEFT JOIN person AS mother ON base.bio_mother_uuid = mother.uuid
LEFT JOIN person as father ON base.bio_father_uuid = father.uuid;

我希望将行解组到这个 Go 结构中

type Person struct{
  UUID string
  Givenname string
  Bio_mother *Person
}

如果我使用上面的查询进行查询,我会为每个没有母亲或父亲数据的人得到 NULL(这很好),但是在构建母亲/父亲数据时,我最终会构建不完整的

Person
实例因为我在查询中没有“祖父母”数据。我可以通过在代码中实现逻辑来解决这个问题,以再次查询是否存在 UUID,但这比在查询中以某种方式执行此操作会显着减慢程序速度。

这对我来说是一个学习项目,但我不完全确定要谷歌什么才能找到一个好的答案。

如何调整查询来处理我的用例并仅返回一行?它与递归 CTE 有关,但在查看了一些 PostgreSQL 教程页面后,我不完全确定如何实现它

编辑: 我意识到丢失数据意味着必须动态命名这些列,或者可能是一些代码?也许是附加前缀?在此示例输出中,我仅使用“别名”。请注意格特鲁德如何缺少母数据,行上有间隙。这是因为该模式始终是母亲优先,不确定我是否同意该模式的建模有意义,但这是我现在可以针对这些数据提出的模型。如果我的射击方向错误,请告诉我

(为了可读性,在前几次迭代之后,我还把列缩短为“其他内容”,而且我要上没有 Wi-Fi 的火车,所以想准备好这个问题并关闭)

输入和输出示例

示例数据

uuid 名字 姓氏 bio_mother_uuid bio_father_uuid
大卫 史密斯 ab ac
ab 玛丽 史密斯 广告 ae
ac 约翰 史密斯 af ag
广告 感谢
ae 理查德
af 凯蒂 史密斯
ag 马修 史密斯
温斯顿

所需输出

base_uuid base_givenname 基本姓氏 base_bio_mother_uuid base_bio_father_uuid mother_uuid mother_givenname 母亲姓氏 mother_bio_mother_uuid mother_bio_father_uuid father_uuid 父亲_其他东西 alias_uuid alias_otherstuff alias_uuid alias_otherstuff alias_uuid alias_otherstuff alias_uuid alias_otherstuff alias_uuid alias_otherstuff
大卫 史密斯 ab ac ab 玛丽 史密斯 广告 ae ac 约翰·史密斯.. 广告 格特鲁德雪 ae 理查德·斯诺 af 凯蒂·史密斯 ag 马修·史密斯 温斯顿雪
ab 玛丽 史密斯 广告 ae 广告 格特鲁德 ae 理查德·斯诺 温斯顿雪
sql database postgresql go database-design
1个回答
1
投票

注意:这是反映评论中讨论的答案。

给出示例结构/数据:

create table people
(
    uuid            text,
    givenname       text,
    surname         text,
    bio_mother_uuid text,
    bio_father_uuid text
);

insert into people (uuid, givenname, surname, bio_mother_uuid, bio_father_uuid)
values ('aa', 'david', 'smith', 'ab', 'ac'),
       ('ab', 'mary', 'smith', 'ad', 'ae'),
       ('ac', 'john', 'smith', 'af', 'ag'),
       ('ad', 'gertude', 'snow', null, 'ah'),
       ('ae', 'Richard', 'snow', null, null),
       ('af', 'Katie', 'smith', null, null),
       ('ag', 'Mathew', 'smith', null, null),
       ('ah', 'Winston', 'Snow', null, null);   

您可以运行递归 CTE,例如:

with recursive t as (
  select 
       p.uuid    as request_uuid,
       p.uuid                 as uuid,
       p.givenname            as givenname,
       p.surname              as surname,
       p.bio_mother_uuid      as bio_mother_uuid,
       p.bio_father_uuid      as bio_father_uuid,
       0 as generation
       from people p
  union all 
  select 
       child.request_uuid    as request_uuid,
       p.uuid                 as uuid,
       p.givenname            as givenname,
       p.surname              as surname,
       p.bio_mother_uuid      as bio_mother_uuid,
       p.bio_father_uuid      as bio_father_uuid,
       child.generation + 1 as generation       
       from 
       t child join 
       people p on p.uuid = child.bio_mother_uuid or p.uuid = child.bio_father_uuid
  )
  select * from t where request_uuid = 'aa'  and generation < 3  

结果是(小提琴):

request_uuid uuid 名字 姓氏 bio_mother_uuid bio_father_uuid 一代
大卫 史密斯 ab ac 0
ab 玛丽 史密斯 广告 ae 1
ac 约翰 史密斯 af ag 1
广告 感谢 2
ae 理查德 2
af 凯蒂 史密斯 2
ag 马修 史密斯 2

然后,这将提供填充结构所需的所有信息(需要一些自定义代码来执行此操作)。像这样在单个查询中检索所有数据可能比运行多个查询更快(但这取决于许多因素)。

虽然可能有办法在一行中返回所有这些数据,但我觉得这比上面的更难处理(您仍然需要自定义代码来解组,并且必须处理会增加复杂性)不同数量的列)。一种替代方法是返回 JSON 格式的结构(这会使 SQL 复杂化,但会简化 Go 代码)。

© www.soinside.com 2019 - 2024. All rights reserved.