I am considering switching to PostgreSQL, because of the JSON support. However, I am wondering, if the following would be possible with a single query:
Let's say there are two tables:
Table 1) organisations:
ID (INT) | members (JSONB) |
------------+---------------------------------------------------------|
1 | [{ id: 23, role: "admin" }, { id: 24, role: "default" }]|
2 | [{ id: 23, role: "user" }]
Table 2) users:
ID (INT) | name TEXT | email TEXT |
------------+-----------+---------------|
23 | Max | max@gmail.com |
24 | Joe | joe@gmail.com |
Now I want to get a result like this (all i have is the ID of the organisation [1]):
ID (INT) | members (JSONB) |
------------+--------------------------------------------------------|
1 | [{ id: 23, name: "Max", email: "max@gmail.com", role:
"admin" },
{ id: 24, name: "Joe", email: "joe@gmail.com ", role:
"default" }]
(1 row)
I know this is not what JSONB is intended for and that there is a better solution for storing this data in SQL, but I am just curious if it would be possible.
Thanks!
Yes it is possible to meet this requirement with Postgres. Here is a solution for 9.6 or higher.
SELECT o.id,
JSON_AGG(
JSON_BUILD_OBJECT('id', u.id, 'name', u.name, 'email', u.email, 'role', e.usr->'role')
)
FROM organisations o
INNER JOIN LATERAL JSONB_ARRAY_ELEMENTS(o.data) AS e(usr) ON TRUE
INNER JOIN users u ON (e.usr->'id')::text::int = u.id
GROUP BY o.id
See this db fiddle .
Explanation :
the JSONB_ARRAY_ELEMENTS
function splits the organisation json array into rows (one per user) ; it is usually used in combination with JOIN LATERAL
to join the users
table, we access the content of the id
field using the ->
operator
for each user, the JSONB_BUILD_OBJECT
is used to create a new object, by passing a list of values/keys pairs ; most values comes from the users
table, expcepted the role
, that is taken from the organisation json element
the query aggregates by organisation id, using JSONB_AGG
to generate a json array by combining above objects
For more information, you may also have a look at Postgres JSON Functions documentation .
There might be more ways to do that. One way would use jsonb_to_recordset()
to transform the JSON into a record set you can join. Then create a JSON from the result using jsonb_build_object()
for the individual members and jsonb_agg()
to aggregate them in a JSON array.
SELECT jsonb_agg(jsonb_build_object('id', "u"."id",
'name', "u"."name",
'email', "u"."email",
'role', "m"."role"))
FROM "organisations" "o"
CROSS JOIN LATERAL jsonb_to_recordset(o."members") "m" ("id" integer,
"role" text)
INNER JOIN "users" "u"
ON "u"."id" = "m"."id";
What functions are available in detail depends on the version. But since you said you consider switching, assuming a more recent version should be fair.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.