I have a json datatype field to store complex data. JSON data looks like this hash:
{
"0" => {
"origin" => {},
"diff" => {
"type" => "type_1",
...
}
},
"1" => {
"origin" => {
"type" => "type_2",
...
},
"diff" => {
...
}
},
...
}
I've tried to transform json to array to avoid these index keys, but it did not help me.
WITH data_values AS (
SELECT id, array_to_json(array(SELECT t.v from json_each_text(data) as t(k,v))) as array_data
FROM event_logs
)
SELECT * FROM data_values
WHERE array_data->'origin'->>'type' = 'type_3' OR array_data->'diff'->>'type' = 'type_3'
Also, I had an idea to use json_object_keys and iterate over top-level keys to find the necessary key/value pair, but I'm a newbie to PSQL and I have some problems with solving that problem.
A version of PSQL is 11, so the JSON path is not available for me.
Table definition example:
CREATE TABLE event_logs (
id integer,
data json,
created_at timestamp without time zone
);
INSERT INTO event_logs (id, data)
VALUES
(1, '{"0": {"origin": {}, "diff": {"type": "type_1"}}, "1": {"origin": {"type": "type_1"}, "diff": {}}}'),
(2, '{"0": {"origin": {}, "diff": {"type": "type_2"}}, "1": {"origin": {}, "diff": {"type": "type_3"}}}'),
(3, '{"0": {"origin": {}, "diff": {"type": "type_3"}}, "1": {"origin": {"type": "type_2"}, "diff": {}}}')
Important note: there can be a different count of top-level keys.
I want to find records by the key/value pair (for example, type = 'type_3'). It should select records with ID 2 and 3.
Can you help me to do it right?
Storing the objects as an array instead of an integer-indexed object is a good idea, but that doesn't let you skip this level with the -> operators. (Only jsonpath can do that).
You will need to use the json_each iteration inside your WHERE clauses:
SELECT * FROM data_values
WHERE EXISTS(
SELECT *
FROM json_each(data)
WHERE value->'origin'->>'type' = 'type_3'
OR value->'diff'->>'type' = 'type_3'
);
(If you had used an array, json_each would become json_array_elements).
Related
I have a table, let's call it myTable with the following structure
ID | data
____________
uuid | jsonb
The data in the jsonb field is an array structured in the following way:
[
{
"valueA": "500",
"valueB": "ABC",
},
{
"valueA": "300",
"valueB": "CDE",
}
]
What I want to do is transform that data by converting valueB to be an object, with newKey that corresposnds to the current value of "valueB"
This is the result I want:
[
{
"valueA": "500",
"valueB": {"newKey": "ABC"},
},
{
"valueA": "300",
"valueB": {"newKey": "CDE"},
}
]
I tried doing it with the following query:
UPDATE myTable
SET data = (
SELECT jsonb_agg (
jsonb_insert(elems, '{valueB, newKey}', elems->'valueB')
)
FROM jsonb_array_elements(data) elems
);
It doesn't seem to do anything unfortunately.
Another idea I have is to create a new field, initialize it as an object, then delete the old onde and rename the new one, but it seems there must be a way to do what I want directly?
Solved using jsonb_build_object()
UPDATE myTable
SET data = (
SELECT jsonb_agg (
jsonb_insert(elems, '{valueB}', jsonb_build_object('newKey', elems->'valueB'))
)
FROM jsonb_array_elements(data) elems
);
I have a JSONB field in PostgreSQL (12.5) table Data_Source with the data like that inside:
{
"C1": [
{
"id": 13371,
"class": "class_A1",
"inputs": {
"input_A1": 403096
},
"outputs": {
"output_A1": 403097
}
},
{
"id": 10200,
"class": "class_A2",
"inputs": {
"input_A2_1": 403096,
"input_A2_2": 403095
},
"outputs": {
"output_A2": [
[
403098,
{
"output_A2_1": 403101
},
{
"output_A2_2": [
403099,
403100
]
}
]
],
"output_A2_3": 403102,
"output_A2_4": 403103,
"output_A2_5": 403104
}
}
]
}
Could you please suggest me some SQL query to extract outputs from the JSONB field.
What I need to get as a results:
Output:
name
value
output_A1
403096
output_A2
403098
output_A2_1
403101
output_A2_2
403099
output_A2_2
403100
output_A2_3
403102
output_A2_4
403103
output_A2_5
403104
Any ideas?
Whenever an array is encountered, then JSONB_ARRAY_ELEMENTS(), or an object is encountered, then JSONB_EACH() functions might be applied, along with auxiliary JSONB_TYPEOF() function to determine respective types, consecutively. At the end, combine the results whether of type array or object or not through use of UNION ALL such as
WITH j AS
(
SELECT j2.*, JSONB_TYPEOF(j2.value) AS type
FROM t,
JSONB_EACH(jsdata) AS j0(k,v),
JSONB_ARRAY_ELEMENTS(v) AS j1,
JSONB_EACH((j1.value ->> 'outputs')::JSONB) AS j2
), jj AS
(
SELECT key,j1.*,JSONB_TYPEOF(j1.value::JSONB) AS type
FROM j,
JSONB_ARRAY_ELEMENTS(value) AS j0(v),
JSONB_ARRAY_ELEMENTS(v) AS j1
WHERE type = 'array'
), jjj AS
(
SELECT key,j0.v,JSONB_TYPEOF(j0.v::JSONB) AS type,k
FROM jj,
JSONB_EACH(value) AS j0(k,v)
WHERE type IN ('array','object')
)
SELECT key,value
FROM
(
SELECT key,value,type
FROM j
UNION ALL
SELECT key,value,type
FROM jj
UNION ALL
SELECT k,v,type
FROM jjj
) jt
WHERE type NOT IN ('array','object')
UNION ALL
SELECT k,value
FROM jjj,JSONB_ARRAY_ELEMENTS(v) AS j0
WHERE type IN ('array','object')
Demo
We are exploring the JSON feature in SQL Sever and for one of the scenarios we want to come up with a SQL which can return a JSON like below
[
{
"field": {
"uuid": "uuid-field-1"
},
"value": {
"uuid": "uuid-value" //value is an object
}
},
{
"field": {
"uuid": "uuid-field-2"
},
"value": "1". //value is simple integer
}
... more rows
]
The value field can be a simple integer/string or a nested object.
We are able to come up with a table which looks like below:
field.uuid | value.uuid | value|
------------|---------- | -----|
uuid-field-1| value-uuid | null |
uuid-field-2| null | 1 |
... more rows
But as soon as we apply for json path, it fails saying
Property 'value' cannot be generated in JSON output due to a conflict with another column name or alias. Use different names and aliases for each column in SELECT list.
Is it possible to do it somehow generate this? The value will either be in the value.uuid or value not both?
Note: We are open to possibility of if we can convert each row to individual JSON and add all of them in an array.
select
json_query((select v.[field.uuid] as 'uuid' for json path, without_array_wrapper)) as 'field',
value as 'value',
json_query((select v.[value.uuid] as 'uuid' where v.[value.uuid] is not null for json path, without_array_wrapper)) as 'value'
from
(
values
('uuid-field-1', 'value-uuid1', null),
('uuid-field-2', null, 2),
('uuid-field-3', 'value-uuid3', null),
('uuid-field-4', null, 4)
) as v([field.uuid], [value.uuid], value)
for json auto;--, without_array_wrapper;
The reason for this error is that (as is mentioned in the documentation) ... FOR JSON PATH clause uses the column alias or column name to determine the key name in the JSON output. If an alias contains dots, the PATH option creates nested objects. In your case value.uuid and value both generate a key with name value.
I can suggest an approach (probably not the best one), which uses JSON_MODIFY() to generate the expected JSON from an empty JSON array:
Table:
CREATE TABLE Data (
[field.uuid] varchar(100),
[value.uuid] varchar(100),
[value] int
)
INSERT INTO Data
([field.uuid], [value.uuid], [value])
VALUES
('uuid-field-1', 'value-uuid', NULL),
('uuid-field-2', NULL, 1),
('uuid-field-3', NULL, 3),
('uuid-field-4', NULL, 4)
Statement:
DECLARE #json nvarchar(max) = N'[]'
SELECT #json = JSON_MODIFY(
#json,
'append $',
JSON_QUERY(
CASE
WHEN [value.uuid] IS NOT NULL THEN (SELECT d.[field.uuid], [value.uuid] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
WHEN [value] IS NOT NULL THEN (SELECT d.[field.uuid], [value] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
END
)
)
FROM Data d
SELECT #json
Result:
[
{
"field":{
"uuid":"uuid-field-1"
},
"value":{
"uuid":"value-uuid"
}
},
{
"field":{
"uuid":"uuid-field-2"
},
"value":1
},
{
"field":{
"uuid":"uuid-field-3"
},
"value":3
},
{
"field":{
"uuid":"uuid-field-4"
},
"value":4
}
]
I have the following text in one of my Postgres table as TEXT datatype:
[
{"type": "text", "values": ["General"], "valueType": "string", "fieldType": "text", "value": ["General"], "customFieldId": "ee", "name": "customer_group"},
{"type": "text", "values": ["Vienna"], "valueType": "string", "fieldType": "text", "value": ["Vienna"], "customFieldId": "eU", "name": "customer_city"},
{"type": "text", "values": ["Mario"], "valueType": "string", "fieldType": "text", "value": ["Mario"], "customFieldId": "eZ", "name": "first_name"},
{"type": "text", "values": ["2019-06-30"], "valueType": "date", "fieldType": "text", "value": ["2019-06-30"], "customFieldId": "ea", "name": "created_at_date"}
]
I need to split the values of this TEXT field to columns and rows. For that I have converted the TEXT column to JSON as below:
SELECT CAST( "customFieldValues" as JSON) "customFieldValues" FROM fr.contacts
But when I tried to manipulate this JSON value I'm getting NULL as result.
WITH CTE AS(SELECT CAST( "customFieldValues" as JSON) "customFieldValues" FROM fr.contacts
)
SELECT
"customFieldValues" ->>'customer_city' as dd
FROM CTE
Does anyone have any suggestions on this? How to get the column names and it's values in rows. I want to create a TABLE based on this data.
Any suggestions would be of great help.
below is the expected result,
customer_group customer_city first_name created_at_date
General Vienna Mario 2019-06-30
Disclaimer: It is still not clear:
Why is there one element values and one value? What is the difference?
Why are these elements arrays?
step-by-step demo:db<>fiddle
SELECT
MAX(value) FILTER (WHERE column_name = 'customer_group') AS customer_group,
MAX(value) FILTER (WHERE column_name = 'customer_city') AS customer_city,
MAX(value) FILTER (WHERE column_name = 'first_name') AS first_name,
MAX(value) FILTER (WHERE column_name = 'created_at_date') AS created_at_date
FROM (
SELECT
elems ->> 'name' AS column_name,
elems -> 'value' ->> 0 AS value,
data
FROM
mytable,
json_array_elements(data::json) elems
) s
GROUP BY data
Cast text to json with ::json
Expand the JSON array: One row for each element with json_array_elements()
Getting the value: -> 'value' gets the array, ->> 0 gets the text representation of the first array element (the only one here)
Getting the column: ->> 'name' gets the text representation of the column name
Classical pivot algorithm (turning rows to columns) with the FILTER clause.
I have a table items which has a jsonb column data.
The data column is something like this {"name": "aaa", "age": 23, "job": "dev"}.
How do I select items that the data has only the keys name, age?.
You can use the ? and the ?& operators.
For your usecase, it will be:
SELECT * FROM table WHERE (NOT data ? 'job') AND (data ?& array ['name', 'age'])
Use the delete operator -, example:
with items (data) as (
values
('{"name": "aaa", "age": 23}'::jsonb),
('{"name": "aaa", "age": 23, "job": "dev"}'),
('{"name": "aaa", "age": 23, "gender": "f"}')
)
select *
from items
where data - 'name'- 'age' = '{}'
data
----------------------------
{"age": 23, "name": "aaa"}
(1 row)
In Postgres 10+ you can use a text array:
select *
from items
where data - array['name', 'age'] = '{}'