postgresql search jsonb array items - sql

I want to find rows base on data in the jsonb data type column with postgresql, is this the correct syntax to do search with array items?
SELECT * FROM table
WHERE diff_data #> '{"rfc6902": [{"op": "replace", "path": "/status/"}, "value": "0"]}';
output first row in below table
table
id | diff_data
1 | {"rfc6902": [{"op": "replace", "path": "/status", "value": "0"}]}
2 | {"rfc6902": [{"op": "replace", "path": "/status", "value": "1"}]}

Related

Postgres: Is there a way to find rows that don't contain duplicate values?

I'm looking for a simple way to achieve the following.
I have a table with 3 columns, hash, signal_key and signal. Previously signal_key was an object like so:
{
"entities": [
{"type": "MERCHANT", "value": "000000008769252"},
{"type": "MERCHANT", "value": "5499"}],
"signalType": "TRANSACTION_CATEGORY_OVERRIDE",
"signalVersion": "0"
}
I ran a migration script on these columns so now the above signal_key would be this (updated entities.type and signalVersion):
{
"entities": [
{"type": "MERCHANT_ID", "value": "000000008769252"}],
{"type": "MERCHANT_CATEGORY_CODE", "value": "5499"},
"signalType": "TRANSACTION_CATEGORY_OVERRIDE",
"signalVersion": "1"
}
However, after running the migration I now have 193 rows with "signalVersion": "0" and 189 rows with "signalVersion": "1". Is there a way in postgres to figure out which rows weren't duplicated with the updated values (maybe by looking at entity values etc.)?

Creating NUMERIC array from JSONB data in Postgres

I have a Postgres JSONB data which contains an ARRAY of type NUMERIC. I want to extract this ARRAY and store it in a variable of type NUMERIC[]. Here's is my JSONB object.
{
"userIds": [
101,102,103
],
"userRole": {
"id": "1",
"name": "Administrator"
}
}
How can I extract userIds from this JSONB object and store them in NUMERIC[] as I have to iterate on this NUMERIC[]?
Any help would be highly appreciated.
One way is to extract the ids with jsonb_array_elements, parse them to the right data type and aggregate them again in an array, e.g.:
SELECT array_agg(id) FROM (
SELECT
(jsonb_array_elements('{
"userIds": [101,102,103],
"userRole": {
"id": "1",
"name": "Administrator"
}
}'::jsonb->'userIds')::numeric)) j(id);
array_agg
---------------
{101,102,103}
(1 row)
If you want to iterate over these values as rows in your resultset, don't bother with the outer query:
SELECT
jsonb_array_elements('{
"userIds": [101,102,103],
"userRole": {
"id": "1",
"name": "Administrator"
}}'::jsonb->'userIds')::numeric;
jsonb_array_elements
----------------------
101
102
103
(3 rows)

Can't find record filter by zero array item

my_column has type jsonb
here json
{
"id": 4107,
"states": [
{
"dt": "2020-11-06T10:24:30.277+0000",
"id": "order.new"
}
]
}
I need to find all records where states[0].id="order.new" (zero item in array)
I try this
SELECT * FROM history WHERE my_column #> '{states,0, id}'= 'order.new'
limit 10
But I get error:
ERROR: invalid input syntax for type json
LINE 1: SELECT * FROM history WHERE my_column #> '{states,0, id}'= 'or...
The #> operator tests whether the right operand is contained in the left operand. The right operand must be valid Json document or a literal, that's why you're getting a syntax error.
The operator you want is #>, which uses the array as the path for extraction:
# SELECT * FROM history WHERE my_column #> '{states,0, id}' = '"order.new"'
limit 10;
id | my_column
----+-------------------------------------------------------------------------------------
1 | {"id": 4107, "states": [{"dt": "2020-11-06T10:24:30.277+0000", "id": "order.new"}]}
(1 row)
With the #> operator you could do the following, but it would be checking for matching elements in any array position, not only index 0:
# SELECT * FROM history WHERE my_column #> '{"states": [{ "id": "order.new" }]}';
id | my_column
----+-------------------------------------------------------------------------------------------------------------------------------------------------
1 | {"id": 4107, "states": [{"dt": "2020-11-06T10:24:30.277+0000", "id": "order.new"}]}
4 | {"id": 4107, "states": [{"dt": "2020-11-06T10:24:30.333+0000", "id": "order.test"}, {"dt": "2020-11-06T10:24:33.333+0000", "id": "order.new"}]}

How to generate JSON array from multiple rows, then return with values of another table

I am trying to build a query which combines rows of one table into a JSON array, I then want that array to be part of the return.
I know how to do a simple query like
SELECT *
FROM public.template
WHERE id=1
And I have worked out how to produce the JSON array that I want
SELECT array_to_json(array_agg(to_json(fields)))
FROM (
SELECT id, name, format, data
FROM public.field
WHERE template_id = 1
) fields
However, I cannot work out how to combine the two, so that the result is a number of fields from public.template with the output of the second query being one of the returned fields.
I am using PostGreSQL 9.6.6
Edit, as requested more information, a definition of field and template tables and a sample of each queries output.
Currently, I have a JSONB row on the template table which I am using to store an array of fields, but I want to move fields to their own table so that I can more easily enforce a schema on them.
Template table contains:
id
name
data
organisation_id
But I would like to remove data and replace it with the field table which contains:
id
name
format
data
template_id
At the moment the output of the first query is:
{
"id": 1,
"name": "Test Template",
"data": [
{
"id": "1",
"data": null,
"name": "Assigned User",
"format": "String"
},
{
"id": "2",
"data": null,
"name": "Office",
"format": "String"
},
{
"id": "3",
"data": null,
"name": "Department",
"format": "String"
}
],
"id_organisation": 1
}
This output is what I would like to recreate using one query and both tables. The second query outputs this, but I do not know how to merge it into a single query:
[{
"id": 1,
"name": "Assigned User",
"format": "String",
"data": null
},{
"id": 2,
"name": "Office",
"format": "String",
"data": null
},{
"id": 3,
"name": "Department",
"format": "String",
"data": null
}]
The feature you're looking for is json concatenation. You can do that by using the operator ||. It's available since PostgreSQL 9.5
SELECT to_jsonb(template.*) || jsonb_build_object('data', (SELECT to_jsonb(field) WHERE template_id = templates.id)) FROM template
Sorry for poorly phrasing what I was trying to achieve, after hours of Googling I have worked it out and it was a lot more simple than I thought in my ignorance.
SELECT id, name, data
FROM public.template, (
SELECT array_to_json(array_agg(to_json(fields)))
FROM (
SELECT id, name, format, data
FROM public.field
WHERE template_id = 1
) fields
) as data
WHERE id = 1
I wanted the result of the subquery to be a column in the ouput rather than compiling the entire output table as a JSON.

How to query nested arrays in a postgres json column?

I have some json similar to the json below stored in a postgres json column. I'm trying query it to identify some incorrectly entered data. I'm basically looking for addresses where the house description is the same as the house number. I can't quite work out how to do it.
{
"timestamp": "2014-10-23T16:15:28+01:00",
"schools": [
{
"school_id": "1",
"addresses": [
{
"town": "Birmingham",
"house_description": "1",
"street_name": "Parklands",
"addr_id": "4",
"postcode": "B5 8KL",
"house_no": "1",
"address_type": "UK"
},
{
"town": "Plymouth",
"house_description": "Flat a",
"street_name": "Fore Street",
"addr_id": "2",
"postcode": "PL9 8AY",
"house_no": "15",
"address_type": "UK"
}
]
},
{
"school_id": "2",
"addresses": [
{
"town": "Coventry",
"street_name": "Shipley Way",
"addr_id": "19",
"postcode": "CV8 3DL",
"house_no": "662",
"address_type": "UK"
}
]
}
]
}
I have written this sql which will find where the data matches:
select *
FROM title_register_data
where address_data->'schools'->0->'addresses'->0->>'house_description'=
address_data->'schools'->0->'addresses'->0->>'house_no'
This obviously only works on the first address on the first school. Is there a way of querying all of the addresses of every school?
Use jsonb_array_elements() in lateral, join as many times as the depth of a json array which elements you want to compare:
select
schools->>'school_id' school_id,
addresses->>'addr_id' addr_id,
addresses->>'house_description' house_description,
addresses->>'house_no' house_no
from title_register_data,
jsonb_array_elements(address_data->'schools') schools,
jsonb_array_elements(schools->'addresses') addresses
where addresses->>'house_description' = addresses->>'house_no';
school_id | addr_id | house_description | house_no
-----------+---------+-------------------+----------
1 | 4 | 1 | 1
(1 row)