在 postgres 上创建深度嵌套的 JSON 结构而无需编写非常复杂的缩进查询的简单方法是什么?

Mic*_*ton 5 postgresql json

我将 postgres 数据库中的几个表连接在一起,并将右连接表中的值作为左连接表中的聚合 JSON 结构返回。但是我发现连接的表越多,该查询就会变得越复杂。例如:

select row_to_json(output)
from (
    select image_type.name,
    (
        select json_agg(instances)
        from (
            select image_instance.name, (
                select json_agg(versions)
                from (
                    select image_version.name
                    from image_version
                    where image_version.image_instance_id = image_version.image_instance_id
                ) versions
            ) AS versions
            from image_instance
            where image_instance.image_type_id = image_type.image_type_id
        ) instances
    ) AS images
    from image_type
) output;
Run Code Online (Sandbox Code Playgroud)

我在这里加入了三个表,但是我想再添加几个表,但是代码很快就会变得笨拙且难以维护。有没有一种简单的方法来生成这些类型的聚合连接?

Pat*_*ick 3

首先,在组合来自多个表的数据时,JSON 与常规字段没有什么不同:事情很快就会变得复杂。然而,有一些技巧可以让事情变得易于管理:

1. 菊花链功能

无需独立处理每个函数的输出,您可以在单个语句中将一个函数的输出作为下一个函数的输入。在您的示例中,这意味着您会丢失每个聚合级别的子选择级别,并且您可以忘记别名。你的例子变成:

select row_to_json(row(image_type.name, (
       select json_agg(image_instance.name, (
              select json_agg(image_version.name)
              from image_version
              where image_version.image_instance_id = image_instance.id) -- join edited
       from image_instance
       where image_instance.image_type_id = image_type.image_type_id))))
from image_type;
Run Code Online (Sandbox Code Playgroud)

2. 不要使用标量子查询

这可能是个人品味的问题,但标量子查询往往难以阅读(和编写:您在最里面的标量子查询的连接条件中有一个明显的错误,只是为了说明我的观点)。请改用具有显式连接和聚合的常规子查询:

select row_to_json(row(it.name, iiv.name))
from image_type it
join (select image_type_id, json_agg(name, iv_name) as name
      from image_instance ii
      join (select image_instance_id, json_agg(name) as iv_name
            from image_version group by 1) iv on iv.image_instance_id = ii.id
      group by 1) iiv using (image_type_id);
Run Code Online (Sandbox Code Playgroud)

3.模块化

就在文档开头的教程部分(强烈推荐阅读,无论您认为自己有多熟练):

充分使用视图是良好 SQL 数据库设计的一个关键方面。

create view iv_json as
    select image_instance_id, json_agg(name) as iv_name
    from image_version
    group by 1;

create view ii_json as
    select image_type_id, json_agg(name, iv_name) as name
    from image_instance
    join iv_json on image_instance_id = image_instance.id
    group by 1;
Run Code Online (Sandbox Code Playgroud)

您的主要查询现在变为:

select row_to_json(row(it.name, ii.name))
from image_type it
join ii_json ii using (image_type_id);
Run Code Online (Sandbox Code Playgroud)

等等...

这显然是迄今为止最容易编码、测试和维护的。性能在这里不是问题:查询优化器会将所有链接的视图扁平化为单个执行计划。

最后注意:如果您使用的是 PG9.4+,您可以使用json_build_object()代替row_to_json()以获得更清晰的输出。