I am parsing data from JSON with UNION ALL, but I need repeated it more times. Try to use LOOP, but it doesn't works. :(
I need to parse every element from array in JSON to rows from statements.
I change the number of element in: statements::json->0 and than UNION the data.
The CODE I want to replace with some LOOP:
(
SELECT
execution_entry_id,
account,
trim('"' FROM (account::json->'startBalance')::text) AS startBalance,
trim('"' FROM (account::json->'endBalance')::text) AS endBalance
FROM (
SELECT
execution_entry_id,
statements::json->0 AS account
FROM(
SELECT
e.id AS execution_entry_id,
response_body,
response_body::json->'statements' AS statements
FROM stage_cz.cb_data_execution_entry e
LEFT JOIN stage_cz.cb_data_execution_entry_details d
ON d.execution_entry_id = e.id
WHERE provider_name = 'sokordiatech'
) a
) b WHERE account IS NOT NULL
)
UNION ALL
(
SELECT
execution_entry_id,
account,
trim('"' FROM (account::json->'startBalance')::text) AS startBalance,
trim('"' FROM (account::json->'endBalance')::text) AS endBalance
FROM (
SELECT
execution_entry_id,
statements::json->1 AS account
FROM(
SELECT
e.id AS execution_entry_id,
response_body,
response_body::json->'statements' AS statements
FROM stage_cz.cb_data_execution_entry e
LEFT JOIN stage_cz.cb_data_execution_entry_details d
ON d.execution_entry_id = e.id
WHERE provider_name = 'sokordiatech'
) a
) b WHERE account IS NOT NULL
)
I try to use:
do $$
declare
counter integer := 0;
begin
while counter < 10 loop
SELECT
execution_entry_id,
statements::json->counter AS account
FROM(
SELECT
e.id AS execution_entry_id,
response_body,
response_body::json->'statements' AS statements
FROM stage_cz.cb_data_execution_entry e
LEFT JOIN stage_cz.cb_data_execution_entry_details d
ON d.execution_entry_id = e.id
WHERE provider_name = 'sokordiatech'
) a;
counter := counter + 1;
end loop;
end$$;
and it ends with error:
ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function inline_code_block line 6 at SQL statement
1 statement failed.
It's possible to get data with LOOP or how instead of 10x UNION ALL, please?
Thanks.
If you are use statements::json->0, so you have Json array in your Json string. For splitting array elements from Json array, you can use jsonb_array_elements_text function on PostgreSQL, this function will be extracting your array elements like as rows. For do this you don't need using loop. For example:
with tbl as (
select
'[
{
"employee_id": 2,
"full_name": "Megan Berry",
"manager_id": 1
},
{
"employee_id": 3,
"full_name": "Sarah Berry",
"manager_id": 1
},
{
"employee_id": 4,
"full_name": "Zoe Black",
"manager_id": 1
},
{
"employee_id": 5,
"full_name": "Tim James",
"manager_id": 1
},
{
"employee_id": 6,
"full_name": "Bella Tucker",
"manager_id": 2
}
]'::jsonb as jsondata
)
select jsondata->1 from tbl;
Result:
{"full_name": "Sarah Berry", "manager_id": 1, "employee_id": 3}
This is getting second element of Json array.
But you need all elements, for getting all elements and extracting key values use this syntax:
with table1 as (
select
'[
{
"employee_id": 2,
"full_name": "Megan Berry",
"manager_id": 1
},
{
"employee_id": 3,
"full_name": "Sarah Berry",
"manager_id": 1
},
{
"employee_id": 4,
"full_name": "Zoe Black",
"manager_id": 1
},
{
"employee_id": 5,
"full_name": "Tim James",
"manager_id": 1
},
{
"employee_id": 6,
"full_name": "Bella Tucker",
"manager_id": 2
}
]'::jsonb as jsondata
)
select
tb1.a1->>'full_name' as fullname,
tb1.a1->>'manager_id' as managerid,
tb1.a1->>'employee_id' as employeeid
from (
select jsonb_array_elements_text(jsondata)::jsonb as a1 from table1
) tb1
Result:
fullname managerid employeeid
Megan Berry 1 2
Sarah Berry 1 3
Zoe Black 1 4
Tim James 1 5
Bella Tucker 2 6
Related
I need to join two tables (products and orders) in mysql with a matching data (order id) and different data (products ids) that should be in a array.
I tried with a simple JOIN but data are not being grouped. Does anyone can give me a light?
SELECT Order.id, Order.userId, Product.id AS productsIds
FROM Orders AS Order
JOIN Products AS Product
ON Order.id = Product.orderId
Result:
[
{
"id": 1,
"userId": 1,
"productsIds": 2
},
{
"id": 1,
"userId": 1,
"productsIds": 5
},
{
"id": 1,
"userId": 1,
"productsIds": 6
},
{
"id": 3,
"userId": 2,
"productsIds": 4
},
{
"id": 2,
"userId": 3,
"productsIds": 3
}
]
Expected:
[
{
"id": 1,
"userId": 1,
"productsIds": [2, 5, 6]
},
{
"id": 3,
"userId": 2,
"productsIds": 4
},
{
"id": 2,
"userId": 3,
"productsIds": 3
}
]
You need to use GROUP BY clause and GROUP_CONCAT in select statement, will give the expected result.
Following is the query.
SELECT Order.id, Order.userId, GROUP_CONCAT(Product.id) AS productsIds FROM Orders AS Order JOIN Products AS Product ON Order.id = Product.orderId GROUP BY Order.id, Order.userId ;
i have two table casetemp and medicication_master
patient has fields
id
name
age
sex
medicineid
1
xyz
23
M
1,2
2
abc
20
f
3
medicine has fields
id
medname
desc
1
crosin
fever tab
2
etzaa
shampoo
3
zaanta
painkiller
i want the mysql left join output as following :
[{
"id":"1",
"name":"xyz",
"age":"23",
"sex":"M",
"medicine_id":"1,2",
"medicine_Data":[
{
"id":"1"
"medname":"crosin",
"desc":"fever tab"
},
{
"id":"2"
"medname":"etzaa",
"desc":"shampoo"
}]
},
{
"id":"2",
"name":"abc",
"age":"20",
"sex":"F",
"medicine_id":"3",
"medicine_Data":[{
"id":"3"
"medname":"zaanta",
"desc":"pain killer"
}]
}]
the query i used is
SELECT json_object(
'id', b.id,
'name',b.name,
'age',b.age,
'sex',b.sex,
'medicine_id',b.medicine_id,
'medicine_data', json_arrayagg(json_object(
'id', pt.id,
'medname', pt.medname,
'desc',pt.desc,
))
)
FROM patient b LEFT JOIN medication_master pt
ON b.medicine_id = pt.id
where b.id=1
GROUP BY b.id;
thanks in advance
I have a json data like this:
[
{"id": 1}, {"id": 3}, {"id": 2, "children": [{"id": 4}, {"id": 5}]}
]
Please help me how to parse this data into relational data:
Column: Id Order/Index ParentId
---------------------------------------
1 1 0
3 2 0
2 3 0
4 4 2
5 5 2
There are a couple of non-trivial things in this request. First is to order the resulting rows by the document position, which is not visible when you use OPENJSON … WITH to project the columns. And the second one is that you need a hierarchical query (assuming there could be multiple levels).
Anyway, something like this:
declare #doc nvarchar(max) = N'[{"id":1},{"id":3},{"id":2,"children":[{"id":4},{"id":5}]}]';
with q as
(
select [key] nodePath,
cast(json_value(d.[value],'$.id') as int) Id,
cast(null as int) ParentId,
cast(json_query(d.[value],'$.children') as nvarchar(max)) children
from openjson(#doc) d
union all
select q.nodePath + '.' + d.[key] nodePath,
cast(json_value(d.[value],'$.id') as int) Id,
q.id ParentId,
cast(json_query(d.[value],'$.children') as nvarchar(max)) children
from q
outer apply openjson(q.children) d
where q.children is not null
)
select Id, row_number() over (order by nodePath) [Order/Index], ParentId
from q
order by [Order/Index]
outputs
Id Order/Index ParentId
----------- -------------------- -----------
1 1 NULL
3 2 NULL
2 3 NULL
4 4 2
5 5 2
(5 rows affected)
I have 3 tables: Users, Articles and Votes
| Users | | Articles | | Votes |
| id | | id | | userId |
| name | | title | | articleId |
| email | | userId | | type |
I want to get users list with Count voteup and Count votedown for each one.
I'm testing this query:
SELECT u.id,u.name,u.email,
(SELECT COUNT(*) FROM votes as v WHERE v.type=1 AND v.articleId IN
(SELECT a.id From articles as a WHERE a.userId = u.id) ) AS totalvoteup,
(SELECT COUNT(*) FROM votes as v WHERE v.type=0 AND v.articleId IN
(SELECT a.id From articles as a WHERE a.userId = u.id) ) AS totalvotedown
FROM users as u
I have the list I want when I test it via phpmyadmin(the results number matches with the number of users in the table), but when I try to get through Node server(from AngularJs or Postman) I'm getting duplicate results:
{
"users": [
[
{
"id": 1,
"name": "John Lennon",
"email": "johnlennon#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 2,
"name": "John Lennon 2",
"email": "johnlennon2#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 3,
"name": "John Lennon 3",
"email": "johnlennon3#gmail.com",
"totalvoteup": 0,
"totalvotedown": 1
},
{
"id": 4,
"name": "John Lennon 4",
"email": "johnlennon4#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
}
],
[
{
"id": 1,
"name": "John Lennon 1",
"email": "johnlennon1#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 2,
"name": "John Lennon 2",
"email": "johnlennon2#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
},
{
"id": 3,
"name": "John Lennon 3",
"email": "johnlennon3#gmail.com",
"totalvoteup": 0,
"totalvotedown": 1
},
{
"id": 4,
"name": "John Lennon 4",
"email": "johnlennon4#gmail.com",
"totalvoteup": 0,
"totalvotedown": 0
}
]
]
}
Any ideas how to resolve this?
Am not a Java guy and am not sure about the problem in question, but the query can be written in better way
SELECT DISTINCT u.id,
u.name,
u.email,
coalesce(totalvoteup,0) as totalvoteup,
coalesce(totalvotedown,0) as totalvotedown
FROM users AS u
LEFT JOIN (SELECT DISTINCT id,userId FROM articles) a
ON a.userId = u.id
LEFT JOIN (SELECT Count(CASE WHEN v.type = 1 THEN 1 END) AS totalvoteup,
Count(CASE WHEN v.type = 0 THEN 1 END) AS totalvotedown,
v.articleId
FROM votes v
GROUP BY v.articleId) v
ON a.id = v.articleId
May not help you to solve the problem just thought of sharing since I like query optimization
I have a table with JSON data in it, and a statement that pulls out an array of ID's for each row...
SELECT items.data->"$.matrix[*].id" as ids
FROM items
This results in something like..
+------------+
| ids |
+------------+
| [1,2,3] |
+------------+
Next I want to select from another table where the ID of that other table is in the array, similar to the WHERE id IN ('1,2,3') but using the JSON array...
Something along the lines of...
SELECT * FROM other_items
WHERE id IN (
SELECT items.data->"$.matrix[*].id" FROM items
);
but it needs some JSON magic and I cant work it out...
Below is a complete answer. You may want a 'use <db_name>;' statement at the top of the script. The point is to show that JSON_CONTAINS() may be used to achieve the desired join.
DROP TABLE IF EXISTS `tmp_items`;
DROP TABLE IF EXISTS `tmp_other_items`;
CREATE TABLE `tmp_items` (`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT, `data` json NOT NULL);
CREATE TABLE `tmp_other_items` (`id` int NOT NULL, `text` nvarchar(30) NOT NULL);
INSERT INTO `tmp_items` (`data`)
VALUES
('{ "matrix": [ { "id": 11 }, { "id": 12 }, { "id": 13 } ] }')
, ('{ "matrix": [ { "id": 21 }, { "id": 22 }, { "id": 23 }, { "id": 24 } ] }')
, ('{ "matrix": [ { "id": 31 }, { "id": 32 }, { "id": 33 }, { "id": 34 }, { "id": 35 } ] }')
;
INSERT INTO `tmp_other_items` (`id`, `text`)
VALUES
(11, 'text for 11')
, (12, 'text for 12')
, (13, 'text for 13')
, (14, 'text for 14 - never retrieved')
, (21, 'text for 21')
, (22, 'text for 22')
-- etc...
;
-- Show join working:
SELECT
t1.`id` AS json_table_id
, t2.`id` AS joined_table_id
, t2.`text` AS joined_table_text
FROM
(SELECT st1.id, st1.data->'$.matrix[*].id' as ids FROM `tmp_items` st1) t1
INNER JOIN `tmp_other_items` t2 ON JSON_CONTAINS(t1.ids, CAST(t2.`id` as json), '$')
You should see the following results:
Starting from MySQL 8.0.13, there is MEMBER OF operator, which does exactly what you're looking for.
The query should be rewritten in the form of JOIN, though:
SELECT o.* FROM other_items o
JOIN items i ON o.id MEMBER OF(i.data->>'$.id')
If you want your query to have better performance, consider using multi-valued indexes on your JSON column.
Using of MEMBER OF() can be explained more clearly on the following example:
CREATE TABLE items ( data JSON );
INSERT INTO items
SET data = '{"id":[1,2,3]}';
That is how you find out whether the value is present in the JSON array:
SELECT * FROM items
WHERE 3 MEMBER OF(data->>'$.id');
+-------------------+
| data |
+-------------------+
| {"id": [1, 2, 3]} |
+-------------------+
1 row in set (0.00 sec)
Note that type of the value matters in this case, unlike regular comparison. If you pass it in a form of string, there will be no match:
SELECT * FROM items
WHERE "3" MEMBER OF(data->>'$.id');
Empty set (0.00 sec)
Although regular comparison would return 1:
SELECT 3 = "3";
+---------+
| 3 = "3" |
+---------+
| 1 |
+---------+
1 row in set (0.00 sec)
Before JSON being introduced in MySQL, I use this:
Ur original data: [1,2,3]
After replace comma with '][': [1][2][3]
Wrap ur id in '[]'
Then use REVERSE LIKE instead of IN: WHERE '[1][2][3]' LIKE
'%[1]%'
Answer to your question:
SELECT * FROM other_items
WHERE
REPLACE(SELECT items.data->"$.matrix[*].id" FROM items, ',', '][')
LIKE CONCAT('%', CONCAT('[', id, ']'), '%')
Why wrap into '[]'
'[12,23,34]' LIKE '%1%' --> true
'[12,23,34]' LIKE '%12%' --> true
If wrap into '[]'
'[12][23][34]' LIKE '%[1]%' --> false
'[12][23][34]' LIKE '%[12]%' --> true
Take care that the accepted answer won't use index on tmp_other_items leading to slow performances for bigger tables.
In such case, I usually use an integers table, containing integers from 0 to an arbitrary fixed number N (below, about 1 million), and I join on that integers table to get the nth JSON element:
DROP TABLE IF EXISTS `integers`;
DROP TABLE IF EXISTS `tmp_items`;
DROP TABLE IF EXISTS `tmp_other_items`;
CREATE TABLE `integers` (`n` int NOT NULL PRIMARY KEY);
CREATE TABLE `tmp_items` (`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT, `data` json NOT NULL);
CREATE TABLE `tmp_other_items` (`id` int NOT NULL PRIMARY KEY, `text` nvarchar(30) NOT NULL);
INSERT INTO `tmp_items` (`data`)
VALUES
('{ "matrix": [ { "id": 11 }, { "id": 12 }, { "id": 13 } ] }'),
('{ "matrix": [ { "id": 21 }, { "id": 22 }, { "id": 23 }, { "id": 24 } ] }'),
('{ "matrix": [ { "id": 31 }, { "id": 32 }, { "id": 33 }, { "id": 34 }, { "id": 35 } ] }')
;
-- Put a lot of rows in integers (~1M)
INSERT INTO `integers` (`n`)
(
SELECT
a.X
+ (b.X << 1)
+ (c.X << 2)
+ (d.X << 3)
+ (e.X << 4)
+ (f.X << 5)
+ (g.X << 6)
+ (h.X << 7)
+ (i.X << 8)
+ (j.X << 9)
+ (k.X << 10)
+ (l.X << 11)
+ (m.X << 12)
+ (n.X << 13)
+ (o.X << 14)
+ (p.X << 15)
+ (q.X << 16)
+ (r.X << 17)
+ (s.X << 18)
+ (t.X << 19) AS i
FROM (SELECT 0 AS x UNION SELECT 1) AS a
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS b ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS c ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS d ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS e ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS f ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS g ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS h ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS i ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS j ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS k ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS l ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS m ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS n ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS o ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS p ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS q ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS r ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS s ON TRUE
INNER JOIN (SELECT 0 AS x UNION SELECT 1) AS t ON TRUE)
;
-- Insert normal rows (a lot!)
INSERT INTO `tmp_other_items` (`id`, `text`)
(SELECT n, CONCAT('text for ', n) FROM integers);
Now you cna try again the accepted answer's query, which takes about 11seconds to run (but is simple):
-- Show join working (slow)
SELECT
t1.`id` AS json_table_id
, t2.`id` AS joined_table_id
, t2.`text` AS joined_table_text
FROM
(SELECT st1.id, st1.data->'$.matrix[*].id' as ids FROM `tmp_items` st1) t1
INNER JOIN `tmp_other_items` t2 ON JSON_CONTAINS(t1.ids, CAST(t2.`id` as JSON), '$')
;
And compare it to the faster approach of converting the JSON into a (temporary) table of ids, and then doing a JOIN over it (which lead to instant results, 0.000sec according to heidiSQL):
-- Fast
SELECT
i.json_table_id,
t2.id AS joined_table_id,
t2.`text` AS joined_table_text
FROM (
SELECT
j.json_table_id,
-- Don't forget to CAST if needed, so the column type matches the index type
-- Do an "EXPLAIN" and check its warnings if needed
CAST(JSON_EXTRACT(j.ids, CONCAT('$[', i.n - 1, ']')) AS UNSIGNED) AS id
FROM (
SELECT
st1.id AS json_table_id,
st1.data->'$.matrix[*].id' as ids,
JSON_LENGTH(st1.data->'$.matrix[*].id') AS len
FROM `tmp_items` AS st1) AS j
INNER JOIN integers AS i ON i.n BETWEEN 1 AND len) AS i
INNER JOIN tmp_other_items AS t2 ON t2.id = i.id
;
The most inner SELECT retrieves the list of JSON ids, along with their length (for outer join).
The 2nd inner SELECT takes this list of ids, and JOIN on the integers to retrieve the nth id of every JSON list, leading to a table of ids (instead of a table of jsons).
The outer most SELECT now only has to join this table of ids with the table containing the data you wanted.
Below is the same query using WHERE IN, to match the question title:
-- Fast (using WHERE IN)
SELECT t2.*
FROM tmp_other_items AS t2
WHERE t2.id IN (
SELECT
CAST(JSON_EXTRACT(j.ids, CONCAT('$[', i.n - 1, ']')) AS UNSIGNED) AS id
FROM (
SELECT
st1.data->'$.matrix[*].id' as ids,
JSON_LENGTH(st1.data->'$.matrix[*].id') AS len
FROM `tmp_items` AS st1) AS j
INNER JOIN integers AS i ON i.n BETWEEN 1 AND len)
;