Select certain keys from array of objects - mysql

Say I have a JSON column foo where each value is an array of objects, i.e:
[{"date": somedate, "value": somevalue, "othervaluesidontneed":...},
{"date": somedate, "value": somevalue, "othervaluesidontneed":...},
{"date": somedate, "value": somevalue, "othervaluesidontneed":...},...]
I want to select this column but for each row, to only include the keys date and value, so the returned value is:
[{"date": somedate, "value": somevalue},
{"date": somedate, "value": somevalue},
{"date": somedate, "value": somevalue},...]
Is this possible?

A solution would be to use json_table() (available in MySQ 8.0 only) to expand the array as a set of rows, and then generate a new array of objects that contain only the requested keys with json_arrayagg():
select
json_arrayagg(json_object( 'date', tt.date, 'value', tt.value)) new_js
from
mytable t,
json_table(
js,
"$[*]"
columns(
date datetime path "$.date",
value int path "$.value"
)
) as tt
group by t.id
This requires that some column can be used to uniquely identify a row in the initial table: I called that column id.
Demo on DB Fiddle:
with mytable as (
select 1 id, '[
{ "date": "2019-01-01", "value": 1, "othervaluesidontneed": 12 },
{ "date": "2019-01-02", "value": 2, "othervaluesidontneed": 55 },
{ "date": "2019-01-03", "value": 3, "othervaluesidontneed": 72}
]' js
)
select
json_arrayagg(json_object( 'date', tt.date, 'value', tt.value)) new_js
from
mytable t,
json_table(
js,
"$[*]"
columns(
date datetime path "$.date",
value int path "$.value"
)
) as tt
group by t.id
| new_js |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [{"date": "2019-01-01 00:00:00.000000", "value": 1}, {"date": "2019-01-02 00:00:00.000000", "value": 2}, {"date": "2019-01-03 00:00:00.000000", "value": 3}] |

Related

How to get a specific object in an JSON array in MySQL?

I have a JSON column "jobs" that looks like this:
[
{
"id": "1",
"done": "100",
"target": "100",
"startDate": "123123132",
"lastAction": "123123132",
"status": "0"
},
{
"id": "2",
"done": "10",
"target": "20",
"startDate": "2312321",
"lastAction": "2312321",
"status": "1"
}
]
I want to filter the array by object key values. For example: To find all items that have target > done, status != 0 and lastAction is yesterday to get response like this:
[
{
"id": "1",
"done": "19",
"target": "100",
"startDate": "123123132",
"lastAction": "123123132",
"status": "0"
}
]
I know I can extract the data to a JSON_TABLE() to do the filtering but I don't get the original object back(unless I recreate it back) and the solution is not dynamic.
Can this kind of array filtering can really be done in MySQL?
SELECT JSON_PRETTY(JSON_EXTRACT(jobs.jobs, CONCAT('$[', j.rownum-1, ']'))) AS object
FROM jobs
CROSS JOIN JSON_TABLE(
jobs.jobs, '$[*]' COLUMNS(
rownum for ordinality,
done int path '$.done',
target int path '$.target',
status int path '$.status'
)
) as j
WHERE j.target > j.done AND j.status != 0;
You also mentioned a condition on lastAction, but the example values you gave are not valid dates, so I'll leave that enhancement to you. The example above demonstrates the technique.
Yes it is possible to do it using the JSON_EXTRACT and JSON_SEARCH functions.
Let's say your table is named tbl_Jobs and the jobs column is of type JSON.
SELECT * FROM tbl_Jobs
WHERE JSON_EXTRACT(jobs, "$[*].target") = JSON_EXTRACT(jobs, "$[*].done")
AND JSON_EXTRACT(jobs, "$[*].status") != 0
AND JSON_SEARCH(jobs, 'one', DATE_SUB(CURDATE(), INTERVAL 1 DAY), NULL, "$[*].lastAction") IS NOT NULL

json_search and json_remove in mysql

notice
[
{
"date": "2022. 10. 16.",
"type": 3,
"title": "friend",
"content": "JJ friend",
"parameter": "test"
},
{
"date": "2022. 10. 16.",
"type": 3,
"title": "friend",
"content": "testtest friend",
"parameter": "test1"
}
]
I wanna search and remove in json {"date": "2022. 10. 16.","type": 3,"title": "friend","content": "testtest friend","parameter": "test1"} where id = 'test2'
There's a separate column for the ID.
update UserTable set notice = json_remove(notice, json_search(notice, 'one',
'{"date": "2022. 10. 16.","type": 3,"title": "friend","content": "testtest friend","parameter": "test1"}')) where id = 'test2'";
SELECT JSON_ARRAYAGG(jsonvalue)
FROM src_table
CROSS JOIN JSON_TABLE(src_table.notice,
'$[*]' COLUMNS (jsonvalue JSON PATH '$')) jsontable
WHERE jsonvalue <> CAST('{"date": "2022. 10. 16.","type": 3,"title": "friend","content": "testtest friend","parameter": "test1"}' AS JSON)

Count json tags in sql

I have this json strings
[{"count": 9, "name": "fixkit", "label": "Repair Kit"}, {"count": 1, "name": "phone", "label": "Telefoon"}]
[{"count": 3, "name": "phone", "label": "Telefoon"}]
[{"count": 5, "name": "kunststof", "label": "Kunststof"}, {"count": 6, "name": "papier", "label": "Papier"}, {"count": 2, "name": "metaal", "label": "Metaal"}, {"count": 2, "name": "inkt", "label": "Inkt"}, {"count": 3, "name": "kabels", "label": "Kabels"}, {"count": 2, "name": "klei", "label": "Klei"}, {"count": 2, "name": "glas", "label": "Glas"}, {"count": 12, "name": "phone", "label": "Telefoon"}]
[{"count": 77, "name": "weed", "label": "Cannabis"}, {"count": 1, "name": "firework1", "label": "Vuurpijl 1"}]
And know i want the following output
Phone | Number of phones (in this case: 16)
Fixkit | Number of fixkits (in this case: 9)
I wanted to do this with a sql query. If you know how to do this, thanks in advance!
If you're not using MySQL 8, this is a bit more complicated. First you have to find a path to a name element that has the value phone (or fixkit); then you can replace name in that path with count and extract the count field from that path; these values can then be summed:
SELECT param, SUM(JSON_EXTRACT(counts, REPLACE(JSON_UNQUOTE(JSON_SEARCH(counts, 'one', param, NULL, '$[*].name')), 'name', 'count'))) AS count
FROM data
CROSS JOIN (
SELECT 'phone' AS param
UNION ALL
SELECT 'fixkit'
) params
WHERE JSON_SEARCH(counts, 'one', param, NULL, '$[*].name') IS NOT NULL
GROUP BY param
Output:
param count
fixkit 9
phone 16
Demo on dbfiddle
If you are running MySQL 8.0, you can unnest the arrays into rows with json_table(), then filter on the names you are interested in, and aggregate.
Assuming that your table is mytable and that the json column is called js, that would be:
select j.name, sum(j.cnt) cnt
from mytable t
cross join json_table (
t.js,
'$[*]' columns(
cnt int path '$.count',
name varchar(50) path '$.name'
)
) j
where j.name in ('phone', 'fixkit')
group by j.name
Demo on DB Fiddle:
| name | cnt |
| ------ | --- |
| fixkit | 9 |
| phone | 16 |

PostgreSQL query : distinct count of jsonb values column

I have a table with the name mainapp_project_data which has a jsonb column project_user_data
TABLE
public | mainapp_project_data | table | admin
select project_user_data from mainapp_project_data;
project_user_data
-----------------------------------------------------------------------------------------------------------------
[{"name": "john", "age": "21", "gender": "M"}, {"name": "randy", "age": "23", "gender": "M"}]
[{"name": "donald", "age": "31", "gender": "M"}, {"name": "wick", "age": "32",
"gender": "M"}]
[{"name": "orton", "age": "18", "gender": "M"}, {"name": "russel", "age": "55",
"gender": "M"}]
[{"name": "angelina", "age": "open", "gender": "F"}, {"name": "josep", "age": "21",
"gender": "M"}]
(4 rows)
(END)
I would like to count the distinct values of keys gender and age of JSON.
output format : [{key:count(repeated_values)}]
filtering on `gender` : [{"M":7},{"F":1}]
filtering on `age` : [{"21":2},{"23":1},{"31":1}.....]
WITH flat AS (
SELECT
kv.key,
-- make into a JSON object with a single value and count, e.g., '{"M": 7}'
jsonb_build_object(kv.value, COUNT(*)) AS val_count
FROM mainapp_project_data AS mpd
-- Flatten the JSON arrays into single objects per row
CROSS JOIN LATERAL jsonb_array_elements(mpd.project_user_data) AS unarrayed(udata)
-- Convert to a long, flat list of key-value pairs
CROSS JOIN LATERAL jsonb_each_text(unarrayed.udata) AS kv(key, value)
GROUP BY kv.key, kv.value
)
SELECT
-- de-deplicated object keys
flat.key,
-- aggregation of all values and counts per key
jsonb_agg(flat.val_count) AS value_counts
FROM flat
GROUP BY flat.key
Returns
key | value_counts
--------+---------------------------------------------------------------------------------------------------------------------
gender | [{"M": 7}, {"F": 1}]
name | [{"josep": 1}, {"russel": 1}, {"orton": 1}, {"donald": 1}, {"wick": 1}, {"john": 1}, {"randy": 1}, {"angelina": 1}]
age | [{"18": 1}, {"32": 1}, {"21": 2}, {"23": 1}, {"open": 1}, {"31": 1}, {"55": 1}]
This will provide any key-value pair instance count. If you just want genders and ages, just add a where clause before the first GROUP BY clause.
WHERE kv.key IN ('gender', 'age')
Does something like this work for you?
postgres=# select count(*), (foo->'gender')::text as g from (select json_array_elements(project_user_data) as foo from mainapp_project_data) as j group by (foo->'gender')::text;
count | g
-------+-----
7 | "M"
1 | "F"
(2 rows)
postgres=# select count(*), (foo->'age')::text as g from (select json_array_elements(project_user_data) as foo from mainapp_project_data) as j group by (foo->'age')::text;
count | g
-------+--------
2 | "21"
1 | "32"
1 | "open"
1 | "23"
1 | "18"
1 | "55"
1 | "31"
(7 rows) ```

How to query nested array of jsonb

I am working on a PostgreSQL 11 table with a column of nested and multiple jsonb objects
to simulate the issue: -
CREATE TABLE public.test
(
id integer NOT NULL DEFAULT nextval('test_id_seq'::regclass),
testcol jsonb
)
insert into test (testcol) values
('[{"type": {"value": 1, "displayName": "flag1"}, "value": "10"},
{"type": {"value": 2, "displayName": "flag2"}, "value": "20"},
{"type": {"value": 3, "displayName": "flag3"}, "value": "30"},
{"type": {"value": 4, "displayName": "flag4"}},
{"type": {"value": 4, "displayName": "flag4"}},
{"type": {"value": 6, "displayName": "flag6"}, "value": "40"}]');
I am trying to:
get outer value if type= specific value. e.g. get the value 30, if flag3 is in displayname.
count occurrence of flag4 in inner json
You could use json_to_recordset to parse it:
WITH cte AS (
SELECT test.id, sub."type"->'value' AS t_value, sub."type"->'displayName' AS t_name, value
FROM test
,LATERAL jsonb_to_recordset(testcol) sub("type" jsonb, "value" int)
)
SELECT *
FROM cte
-- WHERE ...
-- GROUP BY ...;
db<>fiddle demo