【发布时间】:2018-04-22 13:29:54
【问题描述】:
这个问题最好用一个例子来解释。因此,如果您在 PostgreSQL 中有 2 个表 category 和 event,如下所示:-
create table category (
id integer primary key,
type varchar(255),
label varchar (255),
parent_id integer
);
insert into category (id, type, label, parent_id)
values (1, 'organisation', 'Google', null),
(2, 'product', 'Gmail', 1),
(3, 'organisation', 'Apple', null),
(4, 'product', 'iPhone', 3),
(5, 'product', 'Mac', 3);
create table event (
id integer primary key,
name varchar (255),
category_id integer
);
insert into event (id, name, category_id)
values (1, 'add', 4),
(2, 'delete', 5),
(3, 'update', 2);
如您所见,category 表非常动态,可以定义类别层次结构。
我想要实现的是选择 event 表的条目并将其与类别连接,但将其展平为 JSON 结构。我可以使用以下查询来说明:-
select e.*,
jsonb_build_object(
c1.type, c1.label,
c2.type, c2.label
) as categories
from event e
left join category c2 on c2.id = e.category_id
left join category c1 on c1.id = c2.parent_id
这将返回:-
+----+--------+-------------+------------------------------------------------+
| id | name | category_id | categories |
+----+--------+-------------+------------------------------------------------+
| 1 | add | 4 | {"organisation": "Apple", "product": "iPhone"} |
| 2 | delete | 5 | {"organisation": "Apple", "product": "Mac"} |
| 3 | update | 2 | {"organisation": "Google", "product": "Gmail"} |
+----+--------+-------------+------------------------------------------------+
但是,这种方法仅适用于 event.category_id 列引用的子类别恰好具有 1 个父类别(2 个级别)。我真正要寻找的是生成categories,无论(a)它没有父类别(即1级类别)或(b)有超过1个父级(例如3级)。例如,如果我将以下行添加到 event 和 category 表中:-
insert into category (id, type, label, parent_id)
values (6, 'module', 'Mobile', 5), /* has 2 parents */
(7, 'organisation', 'AirBNB', null); /* has no parents */
insert into event (id, name, category_id)
values (4, 'event1', 6),
(5, 'event2', 7);
...并从上面运行查询,它将返回:-
ERROR: argument 1: key must not be null
SQL state:
我的直觉是递归 CTE 可以解决这个问题。
更新 1
create or replace function category_array(category_parent_id int) returns setof jsonb as $$
select case
when count(x) > 0 then
jsonb_agg(f.x) || jsonb_build_object (
c.type, c.label
)
else jsonb_build_object (
c.type, c.label
)
end as category_pair
from category c
left join category_array (c.parent_id) as f(x) on true
where c.id = category_parent_id
group by c.id, c.type, c.label;
$$ language sql;
...并使用此 SQL 调用...
select *,
category_array(category_id)
from event;
... 将返回以下 ...
+----+--------+-------------+--------------------------------------------------------------------------+
| id | name | category_id | categories |
+----+--------+-------------+--------------------------------------------------------------------------+
| 1 | add | 4 | [{"organisation": "Apple"}, {"product": "iPhone"}] |
| 2 | delete | 5 | [{"organisation": "Apple"}, {"product": "Mac"}] |
| 3 | update | 2 | [{"organisation": "Google"}, {"product": "Gmail"}] |
| 4 | event1 | 6 | [[{"organisation": "Apple"}, {"product": "Mac"}], {"module": "Mobile"}] |
| 5 | event2 | 7 | {"organisation": "AirBNB"} |
+----+--------+-------------+--------------------------------------------------------------------------+
已经很接近了,但还不够!
【问题讨论】:
标签: json postgresql common-table-expression recursive-query jsonb