通过在数组数组中搜索/比较来返回聚合数组

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

我有2张桌子:

类别

id | name |  | slug   | path | parent_id  | depth
1    name1     slug1    {1}      null       0
2    name2     slug2    {1,2}      1        1
3    name3     slug3    {1,2,3}    2        2
5    nam5      slug5    {5}       null      0
......
9    nam4      slug9    {5,9}       5       1

其中path是int[]array类型,并且像breadcrumb一样工作

项目

   id | name
   1    name1 

在项目和类别之间存在M2M关系

item_categories

 id | item_id  | category_id 
   1        1    |  3
   2        1       9   

在上面的示例中,项目分为3类:

我使用以下SQL:

SELECT c1.id, c1.name, c1.slug, c1.parent_id FROM categories AS c1 
WHERE ARRAY[c1.id] <@ (SELECT c2.path FROM categories AS c2 WHERE c2.id= 
(SELECT category_id FROM item_categories WHERE item_id=8 LIMIT 1)) order by depth

在路径上提取面包屑并且它可以工作。

但我想得到所有面包屑(不只是一个)。删除LIMIT 1并更改= to in我将有一个数组数组,而不仅仅是一个数组,并将触发错误:

用作表达式的子查询返回的多行

这是正常的。

我想要的例子 - 项目在:

cat1 - > cat2 - >cat3
ca5 -> cat9

,以及从数据库(所以我可以循环它们):

[ [{'name':cat1, 'slug':slug1}, {'name':cat2, 'slug':slug2}, {'name':cat3, 'slug':slug3}], [{'name':cat5, 'slug':slug5}, {'name':cat9, 'slug':slug9}]]

dbfiddle:https://dbfiddle.uk/?rdbms=postgres_10&fiddle=f756cfe568d38425dfe25cfec60b1b3f

因此,我不是获得一个面包屑,而是如何得到一个数组o面包屑作为结果?

sql postgresql postgresql-10
1个回答
1
投票

使用json_build_objectunnest和命令json_agg

select
     c.id,
     json_agg(
         json_build_object('name',c2.name,'slug',c2.slug)
         order by p.depth
     )
from categories as c
    inner join lateral unnest(c.path) with ordinality as p(id, depth) on true
    inner join categories as c2 on
        c2.id = p.id
where
    exists (
        select *
        from item_categories as tt
        where
            tt.item_id = 1 and
            tt.category_id = c.id
    )
group by
    c.id

db<>fiddle demo

或者,如果您需要,可以使用表中的depth列:

select
     c.id,
     json_agg(
         json_build_object('name',c2.name,'slug',c2.slug)
         order by c2.depth
     )
from categories as c
    inner join categories as c2 on
        c2.id = any(c.path)
where
    exists (
        select *
        from item_categories as tt
        where
            tt.item_id = 1 and
            tt.category_id = c.id
    )
group by
    c.id

db<>fiddle demo

我不喜欢json_build_object的是你必须明确地将你的列命名为双重工作,所以我试图改用to_json。它是有效的,但老实说,当表的别名作为参数传递给函数时,我并不熟悉这种语法(参见Using Composite Types in Queries),如果没有lateral join,它就无法工作:

select
     c.id,
     json_agg(to_json(d) order by c2.depth)
from categories as c
    inner join categories as c2 on
        c2.id = any(c.path)
    cross join lateral (select c.name, c.slug) as d
where
    exists (
        select *
        from item_categories as tt
        where
            tt.item_id = 1 and
            tt.category_id = c.id
    )
group by
    c.id

db<>fiddle demo

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