通过 searching/comparing 在数组数组中返回聚合数组

Returning an agregated array by searching/comparing in an array of arrays

我有 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

其中路径是一个 int[]array 类型并且像面包屑一样工作

   id | name
   1    name1 

Item 和 Category 之间存在 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 我将得到一个数组数组,而不仅仅是一个数组,并且会触发错误:

more than one row returned by a subquery used as an expression

这是正常的。

我想要的示例 - 项目在:

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

因此,除了获得一个面包屑,我如何才能获得一个面包屑数组作为结果?

使用 json_build_object, unnest and ordered 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

或者如果需要,您可以使用 table 中的 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 is that you have to name your columns explicitly doing double work, so I've tried to useto_json instead. It works, but honestly, I'm not that familiar with this syntax when alias of the table is passed to the function as an argument (see Using Composite Types in Queries) and could not make it work without lateral 加入:

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