ant*_*awn 9 postgresql join array json many-to-many
我有一个带有如下表的 PostgreSQL 9.5.3 DB:
container
id: uuid (pk)
... other data
thing
id: uuid (pk)
... other data
container_thing
container_id: uuid (fk)
thing_id: uuid (fk)
primary key (container_id, thing_id)
Run Code Online (Sandbox Code Playgroud)
Acontainer可以指向任意数量的things(没有重复),并且 athing可以被任意数量的containers指向。
可能有大量的容器和东西(取决于我们有多少客户)。每个容器中可能只有 1 到 10 个东西。我们一次最多只能查询大约 20 个容器。一个容器可以是空的,我需要取回一个空数组。
我需要构建代表容器的 json 对象,如下所示:
{
"id": "d7e1bc6b-b659-432d-b346-29f3a530bfa9",
... other data
"thingIds": [
"4e3ad81b-f2b5-4220-8e0e-e9d53c80a214",
"f26f49e5-76b4-4363-9ffe-9654ba0b0f0d"
]
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,但我通过使用两个查询来做到这一点:
select * from "container" where "id" in (<list of container ids>)
select * from "container_thing" where "container_id" in (<list of container ids>)
Run Code Online (Sandbox Code Playgroud)
然后我按程序为每个容器构建“thingIds”数组。
后来我找到了一个具有相关子查询的解决方案,目前对我来说效果很好。
select *, array(select thing_id from container_thing where container_id = c.id) as "thingIds"
from container c;
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来做到这一点,也许以某种方式使用连接?
似乎它们总是生成一组行,这意味着为指向的container每个行复制数据thing。
Erw*_*ter 15
如果容器可以是空的,则当前接受的解决方案对您不起作用。它必须是一个外部连接才能保留没有匹配的行 - 以获得与您在小提琴中使用的相关子查询等效的结果:
Run Code Online (Sandbox Code Playgroud)select *, array(select thing_id from container_thing where container_id = container.id) as "thingIds" from container
SELECT to_json(sub) AS container_with_things
FROM (
SELECT c.*, json_agg(thing_id) AS "thingIds"
FROM container c
LEFT JOIN container_thing ct ON ct.container_id = c.id
WHERE c.id IN (<list of container ids>)
GROUP BY c.id
) sub;Run Code Online (Sandbox Code Playgroud)
每个容器有多行(您提到了 20 行),通常在加入之前聚合速度更快:
SELECT to_json(sub) AS container_with_things
FROM (
SELECT c.*, ct."thingIds"
FROM container c
LEFT JOIN (
SELECT container_id AS id, json_agg(thing_id) AS "thingIds"
FROM container_thing
WHERE container_id IN (<list of container ids>) -- repeat condition
GROUP BY 1
) ct USING (id)
WHERE c.id IN (<list of container ids>)
) sub;
Run Code Online (Sandbox Code Playgroud)
或者,您可以将找到的ARRAY 构造函数与LEFT JOIN LATERAL:
SELECT to_json(sub) AS container_with_things
FROM (
SELECT c.*, ct."thingIds"
FROM container c
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT thing_id
FROM container_thing
WHERE container_id = c.id
-- ORDER BY thing_id -- optional order for deterministic results
) AS "thingIds"
) ct ON true
WHERE c.id IN (<list of container ids>)
) sub;
Run Code Online (Sandbox Code Playgroud)
可能会更快,但。
SQL小提琴。(扩展@a_horse 的小提琴。)
请注意,空容器的结果在上述三个查询中略有不同:
"thingIds":[null]"thingIds":null"thingIds":[]在 Postgres 9.5(因为您正在使用它)中,您还可以使用 jsonb它的功能和一个更少的子查询:
SELECT jsonb_set(to_jsonb(c), '{thingIds}', "thingIds") AS container_with_things
FROM container c
LEFT JOIN (
SELECT container_id AS id, jsonb_agg(thing_id) AS "thingIds"
FROM container_thing
WHERE container_id IN (<list of container ids>) -- repeat condition
GROUP BY 1
) ct USING (id)
WHERE c.id IN (<list of container ids>);
Run Code Online (Sandbox Code Playgroud)
或者:
SELECT to_jsonb(c) || jsonb_build_object('thingIds', "thingIds") AS container_with_things
FROM ...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
25918 次 |
| 最近记录: |