I have an orders table where I store the summary of an order in a jsonb
column
{"users": [
{"food": [{"name": "dinner", "price": "100"}], "room": "2", "user": "bob"},
{"room": "3", "user": "foo"}
]}
Now I want to query all users
with their food->name
.
I tried the following, but that gives me also user foo, that has no food.
select
jsonb_array_elements(jsonb_array_elements(summary->'users')->'food')->>'name' as food,
jsonb_array_elements(summary->'users')->>'user' as user_name
from orders;
food | user_name
-------+-----------
dinner | bob
dinner | foo
How would I do such a query?
UPDATE
I have also a summery like this with two food options
{"users": [
{"food": [{"name": "dinner", "price": "100"}, {"name": "breakfast", "price": "100"}], "room": "2", "user": "bob"},
{"room": "3", "user": "foo"}
]}
and than I get:
food | user_name
-----------+-----------
dinner | bob
breakfast | foo
ideally I want to get
food | user_name
----------------------+-----------
dinner, breakfast | bob
All right, if you do
SELECT jsonb_array_elements(summary->'users') as users FROM orders;
you get
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ users │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ {"food": [{"name": "dinner", "price": "100"}, {"name": "breakfast", "price": "50"}], "room": "2", "user": "bob"} │
│ {"room": "3", "user": "foo"} │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Let's put this select inside another one, selecting what we need:
SELECT users->'user' as user_name, users->'food'->0->'name' as food FROM (
SELECT jsonb_array_elements(summary->'users') as users FROM orders
) as s;
┌───────────┬──────────┐
│ user_name │ food │
├───────────┼──────────┤
│ "bob" │ "dinner" │
│ "foo" │ (null) │
└───────────┴──────────┘
We're close. We just need to add a WHERE
.
SELECT users->'user' as user_name, users->'food'->0->'name' as food FROM (
SELECT jsonb_array_elements(summary->'users') as users FROM orders
) as s WHERE (users->'food') is not null;
Resulting in
┌───────────┬──────────┐
│ user_name │ food │
├───────────┼──────────┤
│ "bob" │ "dinner" │
└───────────┴──────────┘
If you have more data in your food array like
'{"users": [{"food": [{"name": "dinner", "price": "100"}, {"name" : "breakfast", "price" : "50"}], "room": "2", "user": "bob"}, {"room": "3", "user": "foo"}]}'
You can do
SELECT users->'user' as user_name, jsonb_array_elements(users->'food')->>'name' as food FROM (
SELECT jsonb_array_elements(summary->'users') as users FROM orders
) as s WHERE (users->'food') is not null;
And
┌───────────┬───────────┐
│ user_name │ food │
├───────────┼───────────┤
│ "bob" │ dinner │
│ "bob" │ breakfast │
└───────────┴───────────┘
Rewriting the above query to use Common Table Expressions
WITH users_data AS (
SELECT jsonb_array_elements(summary->'users') as users FROM orders
), user_food AS (
SELECT users->'user' as user_name, jsonb_array_elements(users->'food')->>'name' as food
FROM users_data
WHERE (users->'food') is not null
) SELECT * FROM user_food;
Now we just need to group by user_name
WITH users_data AS (
SELECT jsonb_array_elements(summary->'users') as users FROM orders
), user_food AS (
SELECT users->'user' as user_name, jsonb_array_elements(users->'food')->>'name' as food
FROM users_data
WHERE (users->'food') is not null
) SELECT user_name, array_agg(food) foods FROM user_food GROUP BY user_name;
Final result
┌───────────┬────────────────────┐
│ user_name │ foods │
├───────────┼────────────────────┤
│ "bob" │ {dinner,breakfast} │
└───────────┴────────────────────┘
That's the best I could come up with. Let me know if you find a better way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With