Trying to query product_id and inner JSONB value "quantity_on_hand" with "unit:one". Below is example table
Products table
| product_id | data |
| -------- | -------------|
| 00445 | {"storage"...}| - rest of data specified right below
{
"storage": [
{
"unit": "one",
"quantity": 3,
},
{
"unit": "two",
"quantity": 2,
}
}
I found a query:
SELECT product_id, e -> 'quantity' as quant
FROM Products t,
jsonb_array_elements(t.value->'storage') e
WHERE (product_id) IN (('00445'));
The query returns following output:
product_id | quant
00445 | 3
00445 | 2
Please advise how to set rule: "quantity_on_hand" with "unit:one" to return only:
product_id | quant
00445 | 3
Thanks
You can add a clause for filtering the result of the jsonb_array_elements to only include elements where the JSON key "unit"'s value is "one":
SELECT product_id,
e -> 'quantity' AS quant
FROM Products t,
JSONB_ARRAY_ELEMENTS(t.value -> 'storage') e
WHERE (product_id) IN (('00445'))
AND e ->> 'unit' = 'one';
This should give:
product_id | quant
------------+-------
1 | 3
(1 row)
See https://www.postgresql.org/docs/14/functions-json.html for more information on JSONB operators and functions in Postgres.
Related
I am selecting rows as
`SELECT
jsonb_build_object('platform_id',pl.id,'platform_Name',pl."name") as "platform" from
"platforms" as pl ON pl.id = up."platform_id"
WHERE usrs.id = ${userid}`
I am getting result as JSON
"platform": {
"platform_id": 5,
"platform_Name": "Snapchat"
}
but I want the platform result as "array of object " like below
DESIRED RESULT :
"platform": [{
"platform_id": 5,
"platform_Name": "Snapchat"
}]
How to make write query to get it as array ?
I recreated a similar example with:
create table platforms (id int, name text, user_id int);
insert into platforms values (1, 'snapchat',100),(2, 'fb', 100),(3, 'instagram', 200), (4, 'twitter',100);
The platforms table contains:
id | name | user_id
----+-----------+---------
1 | snapchat | 100
2 | fb | 100
3 | instagram | 200
4 | twitter | 100
(4 rows)
You can solve the problem in 2 steps:
create the json object with id and name using jsonb_build_object
aggregate over user_id using jsonb_agg
the query is
with create_doc as (
SELECT
user_id,
jsonb_build_object('platform_id',id,'platform_Name',name) as platform
from platforms
)
select user_id, jsonb_agg(platform) from create_doc group by user_id;
result
user_id | json_agg
---------+----------------------------------------------------------------------------------------------------------------------------------------------
200 | [{"platform_id": 3, "platform_Name": "instagram"}]
100 | [{"platform_id": 1, "platform_Name": "snapchat"}, {"platform_id": 2, "platform_Name": "fb"}, {"platform_id": 4, "platform_Name": "twitter"}]
(2 rows)
I have 3 tables: patient, patient_form (join table), form that look as follows:
patient
| id | name |
| --- | ------------------- |
| 1 | Bob |
| 2 | Matilda |
patient_form (join table)
| id | patient_id | form_id |
| --- | ---------- | -------
| 1 | 1 |1
| 2 | 2 |1
form
| id | label
| --- | ------------------- |
| 1 | intake |
| 1 | follow up |
I'd like to create a query that selects a patient and chains the relational data to result in the following output:
{
id: 1,
name: "Bob",
forms: [ {label: "intake"}, {label:"followup"} ]
}
I understand that there a way using json_agg to create the desired array of objects unfortunatly I've not been able to get this to work.
I figured this out as follows using json_build_object:
SELECT
ARRAY(
SELECT json_build_object(
'link', link,
'label', (SELECT label FROM form WHERE id = form_id))
FROM patient_form
WHERE patient_form.patient_id = patient.id
) AS "forms"
WHERE patient.id = 1;
Instead of the subquery for forms, you can use JOIN instead. The full implementation is as follows:
Schema (PostgreSQL v15)
CREATE TABLE IF NOT EXISTS patient (
id INT NOT NULL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS form (
id INT NOT NULL PRIMARY KEY,
label TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS patient_form(
id INT NOT NULL PRIMARY KEY,
patient_id INT,
form_id INT,
CONSTRAINT patient_id_fk FOREIGN KEY(patient_id)
REFERENCES patient(id)
ON DELETE CASCADE,
CONSTRAINT form_id_fk FOREIGN KEY(form_id)
REFERENCES form(id)
ON DELETE CASCADE
);
INSERT INTO form(id, label) VALUES (1, 'intake'), (2, 'follow up');
INSERT INTO patient(id, name) VALUES (1, 'Bob'), (2, 'Matilda');
INSERT INTO patient_form(id, patient_id, form_id) VALUES (1, 1, 1), (2, 2, 1);
Query #1
SELECT json_build_object(
'id', p.id,
'name', p.name,
'forms', json_agg(f)
) AS data
FROM patient AS p
JOIN patient_form AS p_f ON p_f.patient_id = p.id
JOIN form AS f ON f.id = p_f.form_id
WHERE p.id = 1
GROUP BY p.id;
data
{"id":1,"name":"Bob","forms":[{"id":1,"label":"intake"}]}
Put simply, the output is:
{
"id": 1,
"name": "Bob",
"forms": [
{
"id": 1,
"label": "intake"
}
]
}
View on DB Fiddle
I have a table with jsonb column datatype
+-----+--------------------------------------------------------------------------------+
| id | value |
+-----+--------------------------------------------------------------------------------+
| 1 | {"t1": "val", "value": [{"id": "1", "name": "abc"},{"id": "2", "name": "xyz"}] |
| 2 | {"t1": "val", "value": [{"id": "2", "name": "xyz"},{"id": "3", "name": "pqr"}] |
+-----+--------------------------------------------------------------------------------*
SELECT 'True' as status
FROM table t
where t.value->'value'->-1 -> 'id' IN ('"2"','"1"')
I want to fetch Ture or false along with each id checked in where clause.
Kindly help me in this query
You need to move the condition into the SELECT list, but you have to iterate over all array elements:
select t.id,
exists(select *
from jsonb_array_elements(t.value -> 'value') as v(item)
where item ->> 'id' in ('1','2')) as status
from the_table t;
With Postgres 12 or later, you can extract all IDs into an array and then use the ?| operator:
select t.id,
jsonb_path_query_array(t.value -> 'value', '$[*].id') ?| array['1','2'] as status
from the_table t;
I have two tables.
Table A
id | json
----+------------------
a | {"st":[{"State": "TX", "Value":"0.02"}, {"State": "CA", "Value":"0.2" ...
----+------------------
b | {"st":[{"State": "TX", "Value":"0.32"}, {"State": "CA", "Value":"0.47" ...
Table B
idx | state| dir
----+-------+----------
1 | TX | 123
----+-------+----------
2 | CA | 15
I want to filter table A using column temp from table B. And Table B will select base upon idx value.
I want to select value from each row when state equal to temporary table which is created from tableB using where idx is certain number
lets say idx is equal to 2. That means I can create temporary table using following sql query
with tempT AS(
SELECT *
FROM tableB
where idx = 2);
This is what I am trying to achieve
idx | state| value
----+-------+----------
2 | CA | 0.2
----+-------+----------
2 | CA | 0.47
How can I do that ?
You should use jsonb_array_elements like:
WITH A AS
(SELECT 'a' AS id,
'{"st":[{"State": "TX", "Value":"0.02"}, {"State": "CA", "Value":"0.2"}]}'::jsonb AS json
UNION SELECT 'b' AS id,
'{"st":[{"State": "TX", "Value":"0.32"}, {"State": "CA", "Value":"0.47"}]}'::jsonb AS json),
B AS
(SELECT 1 AS idx,
'TX' AS state,
123 AS dir
UNION SELECT 2 AS idx,
'CA' AS state,
15 AS dir)
SELECT *
FROM
(SELECT A.id,
jsonb_array_elements(A.json->'st') AS obj
FROM A) AS A
inner JOIN B on B.state = obj->>'State'::text
where B.idx = 2;
I have asked way too many questions on jsonb and still feel a bit lost.
I have the following tables:
CREATE TABLE _data (
id serial PRIMARY KEY
, data jsonb
);
--with the following rows:
pk | data
---|------------------------
1 | {"year": 2012, "model": "honda"}
2 | {"year": 2014, "model": "toyota"}
CREATE TABLE _people (
pk serial PRIMARY KEY
user integer
, data integer
, updated timestamp without time zone
, documents jsonb
);
-- with the following rows:
pk | user | data| updated | documents
----|--------|-----|--------------------------|---------------
1 | 1 | 1 | 2015-08-22 16:05:40.76 | [{"type": "spreadsheet", "title": "mySpreadsheet", "length": 1278, "ignoredKey": "ignoreme"}, {"type": "document", "title": "My Nice Title"}]
2 | 1 | 1 | 2015-08-24 16:03:00 | [{"type": "spreadsheet", "title": "anothersheet", "length": 1400, "ignoredKey": "ignoreme"}, {"type": "document", "title": "here's another document"}]
CREATE TABLE _users (
pk serial PRIMARY KEY
, name text
);
-- with the following example row:
pk | name
-----|------
1 | Jim Bob
I am trying to get the following output (notice I am ignoring some keys in my documents...specifically "ignoredKey":
User | Data | Updated |Documents
---------|----------------------------------|---------------------------|------------
Jim Bob | {"year": 2012, "model": "honda"} |2015-08-22 16:05:40.764122 | [{"type": "spreadsheet", "title": "mySpreadsheet", "length": 1278}, {"type": "document", "title": "My Nice Title"}]
Jim Bob | {"year": 2014, "model": "toyota"} |2015-08-24 16:03:00 | [{"type": "spreadsheet", "title": "anothersheet", "length": 1400}, {"type": "document", "title": "here's another document"}]
I have:
SELECT p.pk, u.name, custom_docs, d.data FROM _people p,
jsonb_to_recordset(p.documents) doc(type text, title text, length numeric)
LEFT JOIN _data d ON p.data = d.pk
LEFT JOIN _users u ON p.user = u.pk
Which gives me the error:
ERROR: invalid reference to FROM-clause entry for table "p"
LINE 3: LEFT JOIN _data d ON p.data = d.pk
^
HINT: There is an entry for table "p", but it cannot be referenced from this part of the query.
EDIT #1:
As pointed below I need the "Documents" column that is created to be an array of objects...in the form of:
[{"type": "spreadsheet", "title": "mySpreadsheet", "length": 1278}, {"type": "document", "title": "My Nice Title"}]
Any other format is hard to use once it is retrieved.
EDIT #2:
Dmitry's answer helped get me further along but the "Documents" column contains ALL of the documents from all rows rather than those I need:
WITH docs AS (
SELECT array_agg(to_json(changed_structure)) as changed_json_array
FROM _people p,jsonb_to_recordset(p.documents) AS changed_structure(type text, title text, length numeric)
)
SELECT u.name,d.data,p.updated,docs.changed_json_array FROM docs,_people p
LEFT JOIN _users u ON u.pk = p.user
LEFT JOIN _data d ON d.pk = p.data;
This gives me:
name | data | updated | documents
------- | ----------------------------------| ---------------------------|
Jim Bob | {"year": 2012, "model": "honda"} | 2015-08-22 16:05:40.764122 | {"{\"type\":\"spreadsheet\",\"title\":\"mySpreadsheet\",\"length\":1278}","{\"type\":\"document\",\"title\":\"My Nice Title\",\"length\":null}","{\"type\":\"spreadsheet\",\"title\":\"anothersheet\",\"length\":1400}","{\"type\":\"document\",\"title\":\"here's another document\",\"length\":null}"}
Jim Bob | {"year": 2014, "model": "toyota"} | 2015-08-24-16:03:00 | {"{\"type\":\"spreadsheet\",\"title\":\"mySpreadsheet\",\"length\":1278}","{\"type\":\"document\",\"title\":\"My Nice Title\",\"length\":null}","{\"type\":\"spreadsheet\",\"title\":\"anothersheet\",\"length\":1400}","{\"type\":\"document\",\"title\":\"here's another document\",\"length\":null}"}
The key motive here is to use json_agg(to_json(doc)) from jsonb_to_recordset(p.documents):
select pk, json_agg(to_json(doc)) doc
from _people p, jsonb_to_recordset(p.documents) doc(type text, title text, length numeric)
group by 1
pk | doc
----+-----------------------------------------------------------------------------------------------------------------------------------
1 | [{"type":"spreadsheet","title":"mySpreadsheet","length":1278}, {"type":"document","title":"My Nice Title","length":null}]
2 | [{"type":"spreadsheet","title":"anothersheet","length":1400}, {"type":"document","title":"heres another document","length":null}]
(2 rows)
and appropriate use of the join:
select u.name, d.data, p.updated, s.doc
from _people p
left join _users u on u.pk = p.auser
left join _data d on d.pk = p.data
left join (
select pk, json_agg(to_json(doc)) doc
from _people p, jsonb_to_recordset(p.documents) doc(type text, title text, length numeric)
group by 1
) s on s.pk = p.pk
name | data | updated | doc
---------+----------------------------------+------------------------+-----------------------------------------------------------------------------------------------------------------------------------
Jim Bob | {"year": 2012, "model": "honda"} | 2015-08-22 16:05:40.76 | [{"type":"spreadsheet","title":"mySpreadsheet","length":1278}, {"type":"document","title":"My Nice Title","length":null}]
Jim Bob | {"year": 2012, "model": "honda"} | 2015-08-24 16:03:00 | [{"type":"spreadsheet","title":"anothersheet","length":1400}, {"type":"document","title":"heres another document","length":null}]
(2 rows)
Note: I had to change _users.user to _users.auser.
[changed]
WITH docs AS (
SELECT array_agg(to_json(changed_structure)) as changed_json_array
FROM _people p,jsonb_to_recordset(p.documents) AS changed_structure(type text, title text, length numeric)
)
SELECT u.name,d.data,p.updated,docs.changed_json_array FROM docs,_people p
LEFT JOIN _users u ON u.pk = p.user
LEFT JOIN _data d ON d.id = p.data;