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

时间:2019-04-13 12:11:02

标签: sql postgresql postgresql-10

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

项目和类别之间存在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

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

1 个答案:

答案 0 :(得分: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连接的情况下无法正常工作时,我对这种语法并不熟悉:

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