Postgres 加入 json 数组值
Postgres join on json array values
我有一个tableusers
如下:
| id | name |
| 1 | Marvin Hargraves |
| 2 | Lincoln Clayton |
| 3 | Frank Pearce |
还有一个 table posts
我得到 json 具有属性的列:
| id | attributes | content |
| 11 | {"user_ids":["1", "2"]} | "xyz" |
| 12 | {"user_ids":["1", "3"]} | "xyz" |
数组中的 ID 引用来自 users
table 的用户。
我需要显示用户名而不是像这样的 ID:
| id | users |
as an array
| 11 | ["Marvin Hargraves", "Lincoln Clayton"] |
or string
| 12 | "Marvin Hargraves, Frank Pearce" |
我使用的是 PostgreSQL 版本 10。
我试过这个查询:
SELECT p.id,
(SELECT array_agg(array[u.name])
FROM post AS p
JOIN user u ON u.id = ANY(p.attributes->'user_ids')
GROUP BY p.id) AS users
FROM post p
但是我得到了以下错误:
ERROR: op ANY/ALL (array) requires array on right side
您将需要使用 jsonb_array_elements_text 解压 jsonb 数组,加入用户,然后将其聚合回来:
SELECT p.id, array_agg(name ORDER BY i) as users
FROM post p
-- Use WITH ORDINALITY to preserve the order of the original array
CROSS JOIN LATERAL jsonb_array_elements_text(p.attributes->'user_ids') WITH ORDINALITY AS j(user_id, i)
JOIN users ON users.id = j.user_id::int
GROUP BY p.id
;
这是一个fiddle。
我建议在 LATERAL
子查询中使用 ARRAY constructor:
SELECT p.id, j.users
FROM post p
CROSS JOIN LATERAL (
SELECT ARRAY(
SELECT u.name
FROM jsonb_array_elements_text(p.attributes->'user_ids') WITH ORDINALITY j(user_id, ord)
JOIN users u ON u.id = j.user_id::int
ORDER BY j.ord
) AS users
) j
;
db<>fiddle here
请注意,数组中的 null
个值将被忽略。
在这种情况下,CROSS JOIN
永远不会消除行,因为 ARRAY 构造函数总是 returns 一行,即使 JSON 数组为空或 NULL。
相关:
- PostgreSQL unnest() with element number
- Why is array_agg() slower than the non-aggregate ARRAY() constructor?
- How to turn JSON array into Postgres array?
我有一个tableusers
如下:
| id | name |
| 1 | Marvin Hargraves |
| 2 | Lincoln Clayton |
| 3 | Frank Pearce |
还有一个 table posts
我得到 json 具有属性的列:
| id | attributes | content |
| 11 | {"user_ids":["1", "2"]} | "xyz" |
| 12 | {"user_ids":["1", "3"]} | "xyz" |
数组中的 ID 引用来自 users
table 的用户。
我需要显示用户名而不是像这样的 ID:
| id | users |
as an array
| 11 | ["Marvin Hargraves", "Lincoln Clayton"] |
or string
| 12 | "Marvin Hargraves, Frank Pearce" |
我使用的是 PostgreSQL 版本 10。
我试过这个查询:
SELECT p.id,
(SELECT array_agg(array[u.name])
FROM post AS p
JOIN user u ON u.id = ANY(p.attributes->'user_ids')
GROUP BY p.id) AS users
FROM post p
但是我得到了以下错误:
ERROR: op ANY/ALL (array) requires array on right side
您将需要使用 jsonb_array_elements_text 解压 jsonb 数组,加入用户,然后将其聚合回来:
SELECT p.id, array_agg(name ORDER BY i) as users
FROM post p
-- Use WITH ORDINALITY to preserve the order of the original array
CROSS JOIN LATERAL jsonb_array_elements_text(p.attributes->'user_ids') WITH ORDINALITY AS j(user_id, i)
JOIN users ON users.id = j.user_id::int
GROUP BY p.id
;
这是一个fiddle。
我建议在 LATERAL
子查询中使用 ARRAY constructor:
SELECT p.id, j.users
FROM post p
CROSS JOIN LATERAL (
SELECT ARRAY(
SELECT u.name
FROM jsonb_array_elements_text(p.attributes->'user_ids') WITH ORDINALITY j(user_id, ord)
JOIN users u ON u.id = j.user_id::int
ORDER BY j.ord
) AS users
) j
;
db<>fiddle here
请注意,数组中的 null
个值将被忽略。
在这种情况下,CROSS JOIN
永远不会消除行,因为 ARRAY 构造函数总是 returns 一行,即使 JSON 数组为空或 NULL。
相关:
- PostgreSQL unnest() with element number
- Why is array_agg() slower than the non-aggregate ARRAY() constructor?
- How to turn JSON array into Postgres array?