我有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
因此,除了获得一个面包屑外,我如何才能获得一个数组作为面包屑的结果呢?
答案 0 :(得分:1)
使用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
连接的情况下无法正常工作时,我对这种语法并不熟悉:
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