我有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面包屑作为结果?
使用json_build_object
,unnest
和命令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
或者,如果您需要,可以使用表中的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
我不喜欢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