Extract values from inside JSON column in MySQL based on conditions - mysql

Consider a table in MySQL 5.7.x having a JSON column.
-- CREATE TABLE "plans" -----------------------------------
CREATE TABLE `plans` (
`id` VarChar( 36 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
`name` VarChar( 50 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`structure` JSON NOT NULL,
PRIMARY KEY ( `id` ),
CONSTRAINT `index_exam_plans_on_id` UNIQUE( `id` ),
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci
ENGINE = InnoDB;
The structure column has a JSON. Please find an example structure of the JSON below:
{
"33aa1e1c-0c95-4860-9b71-ccd13f393dd0": {
"name": "Term 1",
"tags": [
"Term"
],
"type": "default",
"uuid": "33aa1e1c-0c95-4860-9b71-ccd13f393dd0",
"is_locked": false
},
"cb896a12-f07c-4bcc-9c22-7bdfa585f5f7": {
"name": "English",
"tags": [
"Paper",
"Course Paper"
],
"type": "course_paper",
"uuid": "cb896a12-f07c-4bcc-9c22-7bdfa585f5f7",
"course_id": 1,
"is_locked": false
},
"e6d2f9fb-0429-42b2-b704-c438e1695044": {
"name": "Written Work",
"tags": [
"Paper",
"Regular Paper"
],
"type": "regular_paper",
"uuid": "e6d2f9fb-0429-42b2-b704-c438e1695044",
"course_id": 2,
"is_locked": false
},
"d0d3eeff-9ffb-4f35-b0fb-b94373d1fe5b": {
"name": "Summative Assessment",
"tags": [
"Exam"
],
"type": "default",
"uuid": "d0d3eeff-9ffb-4f35-b0fb-b94373d1fe5b",
"is_locked": false
},
"0952e100-cd4a-473e-bd24-2370e0dfcc1c": {
"name": "Speaking skills",
"tags": [
"Paper",
"Regular Paper"
],
"type": "regular_paper",
"uuid": "0952e100-cd4a-473e-bd24-2370e0dfcc1c",
"course_id": 5,
"is_locked": false
}
}
Points to note:
The JSON is an object, containing any number of key-value pairs. The example structure has 5 key-value pairs.
The "value" in each key-value pair is another JS object of key-value pairs. Let's call this the "inner JSON".
Every inner JSON has a key called type.
In the example structure above, the first and fourth inner JSONs have default as the value for the type key.
The requirement
I want to find all the inner JSONs which have type set to default. This should be done in just SQL. For the example above, the output should be just the first and fourth inner JSONs.
How do I do this?
I have tried the following, but it does not return the inner JSONs based on the condition. It returns every inner JSON in the structure column.
select JSON_EXTRACT(structure, '$.*') from plans
where name = 'Academic'
and JSON_CONTAINS( structure->'$.*.type', '"default"' );
Could somebody give the right way to go about this?

Related

How to write JSON to Mysql?

sorry for my bad english.
I am inserting a json into mysql like this:
set #json = '[{"name":"ivan","city":"london","kurs":"1", },{"name":"lena","city":"tokio","kurs":"5"},{"name":"misha","city":"kazan","kurs":"3"}]';
select * from json_table(#json,'$[*]' columns(name varchar(20) path '$.name',
city varchar(20) path '$.city',
kurs varchar(20) path '$.kurs')) as jsontable;
But now there is a task to insert an unknown number of additional properties:
set #json = '[{"name":"ivan","city":"london","kurs":"1","options": [{
"ao_id": 90630,
"name": "Высота предмета",
"value": "3.7 см"
}, {
"ao_id": 90673,
"name": "Ширина предмета",
"value": "4 см"
}, {
"ao_id": 90745,
"name": "Ширина упаковки",
"value": "4 см"
}]},{"name":"lena","city":"tokio","kurs":"5", "options": [{
"ao_id": 90630,
"name": "Высота предмета",
"value": "9.7 см"
}]},{"name":"misha","city":"kazan","kurs":"3", "options": [{
"ao_id": 90999,
"name": "Высота",
"value": "5.7 см"
}]}]';
How can I best do this so that I can access the table in the future (search, index, output)?

Create json object from tree structure in Postgresql

I have a nested tree data structure of groups and layers that need to be generated from a couple of joined database tables and then be stored as JSON in a PostgreSQL (version 12.8) database. Groups can contain layer objects, but also (sub)groups. The desired JSON output (generated via SQL and/or function) would look like shown below.
[{
"title": "Folder 1",
"type": "group",
"folded": false,
"layers": [{
"title": "Layer 1",
"type": "WMS",
"visible": true
},
{
"title": "Folder 2",
"type": "group",
"folded": true,
"layers": [{
"title": "Layer 2",
"type": "WMS",
"visible": true
},
{
"title": "Layer 3",
"type": "WMS",
"visible": true
},
{
"title": "Folder 3",
"type": "group",
"folded": true,
"layers": [{
"title": "Layer 4",
"type": "WMS",
"visible": false
},
{
"title": "Layer 5",
"type": "WMS",
"visible": true
}
]
}
]
}
]
}]
These are the database tables and sample data:
CREATE TABLE IF NOT EXISTS jtest.folders
(
id serial,
item_id integer,
title text,
folded boolean NOT NULL
);
CREATE TABLE IF NOT EXISTS jtest.layers
(
id serial,
item_id integer,
title text,
type text,
visible boolean
);
CREATE TABLE IF NOT EXISTS jtest."connect"
(
id serial,
item_id integer,
child_id integer,
layer boolean DEFAULT true
);
--data
INSERT INTO jtest.folders(id, item_id, title, folded) VALUES
(1,1,'Folder 1',false),(2,2,'Folder 2', true),(3,3,'Folder 3', true);
INSERT INTO jtest.layers(item_id,id,title,type,visible) VALUES
(1,4,'Layer 1','WMS',true),(2,5,'Layer 2','WMS',true),(3,6,'Layer 3','WMS',true),(4,7,'Layer 4','WMS',true),(5,8,'Layer 5','WMS',true);
INSERT INTO jtest.connect (item_id, child_id, layer) VALUES
(1,4,true),(1,2,false),(2,5,true),(2,6,true),(2,3,false),(3,7,true),(3,8,true);
I partially succeeded to generate a JSON output for a simple folder-layer tree using queries like the one below, but could not figure out how to correctly handle folders nested into a list of layers (aka sub folders).
SELECT jsonb_agg(sub)
from (
SELECT f.id, f.folded,f.item_id,f.title, to_jsonb(array_agg(l.*)) as layers
FROM jtest.folders f
JOIN jtest.connect c ON f.item_id = c.item_id
JOIN jtest.layers l ON l.item_id = c.child_id
where c.layer is true
GROUP BY f.id,f.folded,f.item_id,f.title
ORDER BY f.item_id ) sub;
Any ideas or examples on how to solve this ?

How do you use JSON_QUERY with null json array inside of json object?

SELECT JSON_query([json], '$') from mytable
Returns fine the contents of [json] field
SELECT JSON_query([json], '$.Guid') from mytable
Returns null
SELECT JSON_query([json], '$.Guid[1]') from mytable
Returns null
I've also now tried:
SELECT JSON_query([json], '$[1].Guid')
SELECT JSON_query([json], '$[2].Guid')
SELECT JSON_query([json], '$[3].Guid')
SELECT JSON_query([json], '$[4].Guid')
and they all return null
So I'm stuck as to figuring out how create the path to get to the info. Maybe SQL Server json_query can't handle the null as the first array?
Below is the string that is stored inside of the [json] field in the database.
[
null,
{
"Round": 1,
"Guid": "15f4fe9d-403c-4820-8e35-8a8c8d78c33b",
"Team": "2",
"PlayerNumber": "78"
},
{
"Round": 1,
"Guid": "8e91596b-cc33-4ce7-bfc0-ac3d1dc5eb67",
"Team": "2",
"PlayerNumber": "54"
},
{
"Round": 1,
"Guid": "f53cd74b-ed5f-47b3-aab5-2f3790f3cd34",
"Team": "1",
"PlayerNumber": "23"
},
{
"Round": 1,
"Guid": "30297678-f2cf-4b95-a789-a25947a4d4e6",
"Team": "1",
"PlayerNumber": "11"
}
]
You need to follow the comments below your question. I'll just summarize them:
Probably the most appropriate approach in your case is to use OPENJSON() with explicit schema (the WITH clause).
JSON_QUERY() extracts a JSON object or a JSON array from a JSON string and returns NULL. If the path points to a scalar JSON value, the function returns NULL in lax mode and an error in strictmode. The stored JSON doesn't have a $.Guid key, so NULL is the actual result from the SELECT JSON_query([json], '$.Guid') FROM mytable statement.
The following statements provide a working solution to your problem:
Table:
SELECT *
INTO Data
FROM (VALUES
(N'[
null,
{
"Round": 1,
"Guid": "15f4fe9d-403c-4820-8e35-8a8c8d78c33b",
"Team": "2",
"PlayerNumber": "78",
"TheProblem": "doesn''t"
},
{
"Round": 1,
"Guid": "8e91596b-cc33-4ce7-bfc0-ac3d1dc5eb67",
"Team": "2",
"PlayerNumber": "54"
},
{
"Round": 1,
"Guid": "f53cd74b-ed5f-47b3-aab5-2f3790f3cd34",
"Team": "1",
"PlayerNumber": "23"
},
{
"Round": 1,
"Guid": "30297678-f2cf-4b95-a789-a25947a4d4e6",
"Team": "1",
"PlayerNumber": "11"
}
]')
) v (Json)
Statements:
SELECT j.Guid
FROM Data d
OUTER APPLY OPENJSON(d.Json) WITH (
Guid uniqueidentifier '$.Guid',
Round int '$.Round',
Team nvarchar(1) '$.Team',
PlayerNumber nvarchar(2) '$.PlayerNumber'
) j
SELECT JSON_VALUE(j.[value], '$.Guid')
FROM Data d
OUTER APPLY OPENJSON(d.Json) j
Result:
Guid
------------------------------------
15f4fe9d-403c-4820-8e35-8a8c8d78c33b
8e91596b-cc33-4ce7-bfc0-ac3d1dc5eb67
f53cd74b-ed5f-47b3-aab5-2f3790f3cd34
30297678-f2cf-4b95-a789-a25947a4d4e6

Convert MySQL query result to JSON

The project I'm working on requires to save all of the DB operations. So when there will be added new user I've to log the date, operation type like 'INSERT', 'UPDATE', 'DELETE' and all user data. The project is in the development phase so the columns in User table are changing.
This what I plan to do is to select the new user data from the Users table and insert them to UserLog table as a JSON column.
Is it possible to convert SELECT * FROM table_name to JSON format?
I know that there is a possibility to convert separated columns by JSON_OBJECT function, but as I mentioned above, the columns are floating so I would be forced to change the JSON_OBJECT names each time I change anything in the main table. And there are a lot of tables!
It should work like this:
CREATE TABLE Users (
id INT(1) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
firstName VARCHAR(30) NOT NULL,
lastName VARCHAR(30) NOT NULL,
email VARCHAR(50),
)
The query:
SELECT * FROM Users;
Should return:
[
{
"id": 1,
"firstName": "Lucas",
"lastName": "Smith",
"email": "lucas#def.com"
},
{
"id": 2,
"firstName": "Ben",
"lastName": "Kovalsky",
"email": "ben#def.com"
},
...
]
Is there a simple solution to solve this problem? If not, what is your strategy for logging DB operations?
I'm not up to date with MySQL as I switched over to PostgreSQL but I found that the recent MySQL, from version 8, supports JSON:
SELECT JSON_ARRAYAGG(
JSON_OBJECT(
'id', `id`,
'firstName', `firstName`,
'lastName', `lastName`,
'email', `email`
)
)
FROM Users;
should work.
Edit, sources:
https://dev.mysql.com/doc/refman/8.0/en/json.html#json-values
https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_json-arrayagg
I know this is an old thread, but for anyone still facing this issue, there is a way to convert the result set into json without knowing the column names. The key is to get the names of the columns in a string like 'column_1', column_1, 'column_2', column_2, ... and then use this string in a prepared query.
SET #column_name_string_for_query = "";
SHOW COLUMNS
FROM your_table_name
WHERE #column_name_string_for_query := TRIM(", " FROM CONCAT("'", Field, "', ", Field, ", ", #column_name_string_for_query));
SET #query_string = concat("
SELECT JSON_ARRAYAGG(JSON_OBJECT(", #column_name_string_for_query, "))
FROM your_table_name"
);
PREPARE statement FROM #query_string;
EXECUTE statement;
DEALLOCATE PREPARE statement;
You could also get the column names from INFORMATION_SCHEMA.COLUMNS, but that only works for tables that are not temporary tables. The solution above works for both temporary tables and normal tables.
You could also save this as a stored procedure for ease of use.
Normally converting the output to JSON or any other format is a job for the programming language or your mySQL IDE, but there is a way also from mySQL.
https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-json-output.html
https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-json-wrapping.html
Directly from documentation:
MySQL localhost:33060+ ssl world_x JS > shell.options.set('resultFormat','json')
MySQL localhost:33060+ ssl world_x JS > session.sql("select * from city where countrycode='AUT'")
{
"ID": 1523,
"Name": "Wien",
"CountryCode": "AUT",
"District": "Wien",
"Info": {
"Population": 1608144
}
}
{
"ID": 1524,
"Name": "Graz",
"CountryCode": "AUT",
"District": "Steiermark",
"Info": {
"Population": 240967
}
}
{
"ID": 1525,
"Name": "Linz",
"CountryCode": "AUT",
"District": "North Austria",
"Info": {
"Population": 188022
}
}
{
"ID": 1526,
"Name": "Salzburg",
"CountryCode": "AUT",
"District": "Salzburg",
"Info": {
"Population": 144247
}
}
{
"ID": 1527,
"Name": "Innsbruck",
"CountryCode": "AUT",
"District": "Tiroli",
"Info": {
"Population": 111752
}
}
{
"ID": 1528,
"Name": "Klagenfurt",
"CountryCode": "AUT",
"District": "Kärnten",
"Info": {
"Population": 91141
}
}
6 rows in set (0.0031 sec)
Also, adding the JSON_OBJECT, that is available from 5.7+, see the answer here.
mysql> SELECT JSON_OBJECT('id', 87, 'name', 'carrot');
+-----------------------------------------+
| JSON_OBJECT('id', 87, 'name', 'carrot') |
+-----------------------------------------+
| {"id": 87, "name": "carrot"} |
+-----------------------------------------

Update JSON object MySQL

I want to update a JSON object in MySQL.
TABLE
id (int-11, not_null, auto_inc)
labels (json)
JSON Beautify
[
{
"tagname": "FOO",
"category": "CAT_1",
"isnew": "no",
"isdeleted": "no"
},
{
"tagname": "BAR",
"category": "CAT_2",
"isnew": "yes",
"isdeleted": "no"
}
]
I want to add a new TAG element (JSON OBJECT) next to the existing objects, but without SELECTing first the field and updating all the field with text.
I have Google a lot but I can not understand yet the MySQL's JSON treatment. I have just learned how to insert data like this:
INSERT INTO `table_name`(
`id` ,
`labels`
)
VALUES(
null ,
JSON_ARRAY
(
JSON_OBJECT
(
"tagname", "FOO",
"category", "CAT_1",
"isnew", "no",
"isdeleted", "no"
),
JSON_OBJECT
(
"tagname", "BAR",
"category", "CAT_2",
"isnew", "yes",
"isdeleted", "no"
)
)
);
You could use JSON_ARRAY_APPEND:
UPDATE tab
SET labels = JSON_ARRAY_APPEND(labels, '$',
JSON_OBJECT
(
"tagname", "BARX",
"category", "CAT_3",
"isnew", "yes",
"isdeleted", "no"
)
)
WHERE ID = 1;
DB-Fiddle.com demo