Nested JSON in MySQL with filtering - mysql

Here's the JSON that I have in the MySQL table:
{
"qmSubsegmentFlags": [
{
"text": "Accuracy",
"id": 1,
"children": [
{
"text": "Mistranslation",
"id": 2,
"children": [
{
"text": "Terminology",
"id": 3
}
]
},
{
"text": "Omission",
"id": 4
},
{
"text": "Addition",
"id": 5
},
{
"text": "Untranslated",
"id": 6
}
]
}
]
}
So you see, id's and text can be in different levels. I only have the value of the id and have to get the text for this with SQL. Below a JSON path that works for the first level, but not for all levels. Neither does it work in MySQL, since MySQL only supports limited filtering.
$.qmSubsegmentFlags[?(#.id==1)].text
A little SQL that goes into the right direction, but also does not solve the issue.
SELECT JSON_EXTRACT(t.qmSubsegmentFlags, '$.qmSubsegmentFlags[*].text') AS lqa FROM translate5.LEK_task AS t where id = 3719;
I used to be a programmer, but after 4 years of project management, my skills are dead. But I need a proof of concept until monday. Any help would be great!

Here's a solution tested on MySQL 8.0.32.
WITH RECURSIVE tree AS (
SELECT j.id, j.text, j.children
FROM LEK_task CROSS JOIN JSON_TABLE(
JSON_EXTRACT(qmSubsegmentFlags, '$.qmSubsegmentFlags'),
'$[*]' COLUMNS (
text TEXT PATH '$.text',
id INT PATH '$.id',
children JSON PATH '$.children'
)
) AS j
WHERE LEK_task.id = 3719
UNION ALL
SELECT j.id, j.text, j.children
FROM tree CROSS JOIN JSON_TABLE(
children,
'$[*]' COLUMNS(
text TEXT PATH '$.text',
id INT PATH '$.id',
children JSON PATH '$.children'
)
) AS j
)
SELECT id, text FROM tree;
Result:
+------+----------------+
| id | text |
+------+----------------+
| 1 | Accuracy |
| 2 | Mistranslation |
| 4 | Omission |
| 5 | Addition |
| 6 | Untranslated |
| 3 | Terminology |
+------+----------------+
You can add a WHERE id = 1 to the last line of the query to get the row for a specific id.

Related

Converting a Dictionary in MySql 8

I have a JSON structure stored in a MySql table. Now, months later, I have a requirement to join to pieces found deep in the bowels of this JSON string.
{
"id": "000689ba-9891-4d82-ad13-a7b96dc08ec4",
"type": "emp",
"firstName": "Brooke",
"facilities": {
​​​​​"0001": [{​​​​​
"id": 1,
"code": "125",
"name": "DSL",
"type": "MGMTSupport",
"payRate": 18}​​​​​],
"0002": [
{
​​​​​"id": 1,
"code": "100",
"name": "Server",
"type": "FOH",
"payRate": 8
}, {
​​​​​"id": 2,
"code": "320",
"name": "NCFOHTrainer",
"type": "NCHourlyTraining",
"payRate": 14.5
}​​​]
},
"permissions": ["read:availability", "..."],
"primaryJobCode": "150",
"primaryPayRate": 9,
"primaryFacility": "0260"
}​
​​​​The big question is: How do I shape this as a query in MySql when the facilities do not follow a single key/value pattern? i.e.: the key to the first entry is the facilityId so I cannot use a path like '$.0001' and the dictionary value is an array so how do I path that correctly?
select id as EmployeeId
, companyId as cpkEmployeeId
, json_table( `data`
, '$.facilities[*]' COLUMNS( facilityId VARCHAR(10) PATH '$.????'
, NESTED PATH '??? $[*] ???' COLUMNS ( code VARCHAR(10) PATH '$.code'
, payRate DECIMAL(8,4) PATH '$.payRate') facilities
from employee
;
Yea - the above does not work. Any help appreciated.
Desired output?
[Other columns from the table] plus facilityId, code & payrate.
A single row in the native table could produce something like:
id | companyId | facilityId | code | payRate
--------+-----------+------------+------+---------
1 | 324337 | 0001 | 125 | 18.0000
1 | 324337 | 0002 | 100 | 8.0000
1 | 324337 | 0002 | 320 | 14.5000
WITH
cte AS (
SELECT test.id,
test.value,
jsontable.facilityId,
CONCAT('$.facilities."', jsontable.facilityId, '"') path
FROM test
CROSS JOIN JSON_TABLE(JSON_KEYS(test.value, '$.facilities'),
'$[*]' COLUMNS (facilityId CHAR(4) PATH '$')) jsontable
)
SELECT cte.id,
cte.facilityId,
jsontable.code,
jsontable.payRate
FROM cte
CROSS JOIN JSON_TABLE(JSON_EXTRACT(cte.value, cte.path),
'$[*]' COLUMNS (code CHAR(3) PATH '$.code',
payRate DECIMAL(6, 4) PATH '$.payRate')) jsontable
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=18ab7b6f181b61fb53f88a6de6e049be

Subquery returned more than 1 value while running OPENJSON JSON_QUERY

I'm trying to write a query against following table
create table employees (id int identity(1,1) primary key, content nvarchar(max));
insert into employees (content)
values (
N'{
"name": "John Doe",
"skills": [
{
"language": "SQL",
"years": 3
},
{
"language": "C#",
"years": 4
}
]
}'),
(
N'{
"name": "Alice Smith",
"skills": [
{
"language": "JS",
"years": 5
}
]
}
')
;
What I'm up to is to end up with the following result
+ id + name + language + years +
| 1 | John Doe | SQL | 3 |
| 2 | John Doe | C# | 4 |
| 3 | Alice Smith | JS | 3 |
Here is what I came up with
SELECT * FROM
OPENJSON(JSON_QUERY((SELECT content FROM employees), '$.skills'))
WITH (
Language VARCHAR(50) '$.language',
Years int '$.years'
)
You can check it out in dbfiddle
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=8ed626ed8441ea6ccc62b8feec99c227
It works when content column contains a single row, but fails when there is more.
Subquery returned more than 1 value. This is not permitted when the subquery
follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I understand what it means, yet I cannot figure out how to get to it in different way.
I've been trying with OPENJSON and JSON_QUERY
The reason for this error is the JSON_QUERY((SELECT content FROM employees), '$.skills') part of the statement, because JSON_QUERY() expects a JSON string. To solve this error you may try the following statement:
SELECT e.id, j1.name, j2.language, j2.years
FROM employees e
OUTER APPLY OPENJSON(e.content) WITH (
name nvarchar(100) '$.name',
skills nvarchar(max) '$.skills' AS JSON
) j1
OUTER APPLY OPENJSON(j1.skills) WITH (
language nvarchar(100) '$.language',
years int '$.years'
) j2
Result:
id name language years
1 John Doe SQL 3
1 John Doe C# 4
2 Alice Smith JS 5

Json hash save into relational database in rows and columns

I want to read the values from json and need to create a new json so is there any way that
we can save json in table and columns in oracle that will help to perform calculation on that. calculation is too complax.
Here is the json sample and json has many hash and
{
"agri_Expense": {
"input": 6000,
"max": 7500,
"check": 7500
},
"income3": {
"Hiring_income": 239750
},
"Operational_Cost1": [
{
"Field_input3": 10000,
"Minimum": "0.05",
"Check_Input": 26750,
"Tractor_Cost": "Maintenance"
}
]
}
You do not need PL/SQL, and can do it entirely in SQL.
I want to read the values from json [...] so is there any way that
we can save json in table and columns in oracle
Yes, use SQL to create a table:
CREATE TABLE table_name ( json_column CLOB CHECK ( json_column IS JSON ) )
and then INSERT the value there:
INSERT INTO table_name ( json_column ) VALUES (
'{'
|| '"agri_Expense": {"input": 6000,"max": 7500,"check": 7500},'
|| '"income3": {"Hiring_income": 239750},'
|| '"Operational_Cost1": [{"Field_input3": 10000,"Minimum": "0.05","Check_Input": 26750,"Tractor_Cost": "Maintenance"}]'
|| '}'
)
then, if you want individual values, SELECT using JSON_TABLE:
SELECT j.*
FROM table_name t
CROSS JOIN JSON_TABLE(
t.json_column,
'$'
COLUMNS (
agri_expense_input NUMBER PATH '$.agri_Expense.input',
agri_expense_max NUMBER PATH '$.agri_Expense.max',
agri_expense_check NUMBER PATH '$.agri_Expense.check',
income3_hiring_income NUMBER PATH '$.income3.Hiring_income',
NESTED PATH '$.Operational_Cost1[*]'
COLUMNS (
oc1_field_input3 NUMBER PATH '$.Field_input3',
oc1_minimum NUMBER PATH '$.Minimum',
oc1_check_input NUMBER PATH '$.Check_Input'
)
)
) j
Which outputs:
AGRI_EXPENSE_INPUT | AGRI_EXPENSE_MAX | AGRI_EXPENSE_CHECK | INCOME3_HIRING_INCOME | OC1_FIELD_INPUT3 | OC1_MINIMUM | OC1_CHECK_INPUT
-----------------: | ---------------: | -----------------: | --------------------: | ---------------: | ----------: | --------------:
6000 | 7500 | 7500 | 239750 | 10000 | .05 | 26750
db<>fiddle here

Search in mysql json column to check multiple criteria on same index of array

I'm using MYSql server 8.0.17.
I want to get record with uId= 'UR000001' and also with VIEW = 'Y' from the security column(Shown in Table).
Viewid Security
VW0000000002 {"security": [{"uId": "UR000001", "edit": "N", "view": "Y"}, {"uId": "UR000002", "edit": "N", "view": "Y"}]}
VW0000000013 {"security": [{"uId": "UR000001", "edit": "N", "view": "N"}, {"uId": "UR000002", "edit": "N", "view": "Y"}]}
VW0000000014 {"security": [{"uId": "UR000001", "edit": "N", "view": "Y"}, {"uId": "UR000002", "edit": "N", "view": "Y"}]}
JSON_SEARCH function searches all array elements of the record that I don't want.
Here is the query that I had tried but it returns the result with all matching (uID='UR000001' OR View='Y')
SELECT viewid,
Json_search(`security`, 'one', 'UR000001', NULL, '$.security[*].uId'),
Json_search(`security`, 'one', 'Y', NULL, '$.security[*].view')
FROM vw_viewmaster
WHERE Json_search(`security`, 'one', 'UR000001', NULL, '$.security[*].uId')
AND Json_search(`security`, 'one', 'Y', NULL, '$.security[*].view');
Actual Result:(uID='UR000001' OR View='Y')
VW0000000002 "$.security[0].uId" "$.security[0].view"
VW0000000013 "$.security[0].uId" "$.security[1].view"
VW0000000014 "$.security[0].uId" "$.security[0].view"
Expected result:(uID='UR000001' AND View='Y')
VW0000000002 "$.security[0].uId" "$.security[0].view"
VW0000000014 "$.security[0].uId" "$.security[0].view"
In MySQL 8.0, you can use handy JSON function json_table() to convert a json array to rows. You can then search the resultset.
The following query gives you all viewids whose at least one array element with attribute uId is equal to 'UR000001' and attribute view is 'Y':
select v.viewid
from vw_viewmaster v
where exists (
select 1
from json_table(
v.security -> '$.security',
'$[*]'
columns(
uid varchar(50) path '$.uId',
edit varchar(1) path '$.edit',
view varchar(1) path '$.view'
)
) x
where x.uid = 'UR000001' and x.view = 'Y'
);
For your dataset, this produces:
| viewid |
| ------------ |
| VW0000000002 |
| VW0000000014 |
If you want the details of the matching array object(s), then:
select v.viewid, x.*
from vw_viewmaster v
cross join json_table(
v.security -> '$.security',
'$[*]'
columns(
rowid for ordinality,
uid varchar(50) path '$.uId',
edit varchar(1) path '$.edit',
view varchar(1) path '$.view'
)
) x
where x.uid = 'UR000001' and x.view = 'Y'
As a bonus, rowid gives you the index of the matching object in the JSON array (the first object has index 1).
This yields:
| viewid | rowid | uid | edit | view |
| ------------ | ----- | -------- | ---- | ---- |
| VW0000000002 | 1 | UR000001 | N | Y |
| VW0000000014 | 1 | UR000001 | N | Y |
However please note that if more than one object in the array that satisfies the conditions, the above query would generate more than one row per row in the original table (this is why I used exists in the first query).

Parsing JSON data from SQL Server table column

I am trying to parse JSON data from a table in SQL Server 2017. I have a view that returns this data:
| Debrief Name | Version | Answer Question | Answer Options |
+-------------------+-----------+--------------------------+--------------------------------------------------------------------------------------------------------------------------+
| Observer Report | 7 | Division: | {"Options":[{"Display":"Domestic","Value":"Domestic"},{"Display":"International","Value":"International"}]} |
| Observer Report | 7 | Are you on reserve? | {"Options":[{"Display":"Yes - Long Call Line","Value":"Yes"},{"Display":"No","Value":"No"}]} |
| Observer Report | 11 | Crew Position: | {"Options":[{"Display":"CA","Value":"CA"},{"Display":"RC","Value":"RC"},{"Display":"FO","Value":"FO"}]} |
| Observer Report | 11 | Domicile: | {"VisibleLines":2,"Options":[{"Display":"BOS","Value":"BOS"},{"Display":"CLT","Value":"CLT"}]} |
| Training Debrief | 12 | TRAINING CREW POSITION | {"VisibleLines":2,"Options":[{"Display":"CA","Value":"CA"},{"Display":"FO","Value":"FO"}]} |
| Training Debrief | 12 | AIRCRAFT | {"VisibleLines":2,"Options":[{"Display":"777","Value":"777"},{"Display":"767","Value":"767"}]} |
| Security Debrief | 9 | Aircraft Type | {"Options":[{"Display":"MD-80","Value":"MD-80"},{"Display":"777","Value":"777"},{"Display":"767/757","Value":"767/757"}]}|
| News Digest | 2 | Do you read Digest? | {"Options":[{"Display":"Yes","Value":"Yes"},{"Display":"No","Value":"No"}]} |
The Debrief Name column can have multiple records for same debrief name and Version. Also there are multiple versions for each debrief. And for each debrief name and version combination, there are set of Answer Questions and related Answer Options. Now the column Answer Options contain JSON record which I need to parse.
So my initial query that is something like below:
SELECT *
FROM [dbo].<MY VIEW>
WHERE [Debrief Name] = 'Observer Report' AND Version = 11
which would return below data:
| Debrief Name | Version | Answer Question | Answer Options |
+---------------------+--------------+-----------------------+-----------------------------------------------------------------------------------------------------------------+
| Observer Report | 11 | Crew Position: | {"Options":[{"Display":"CA","Value":"CA"},{"Display":"RC","Value":"RC"}]} |
| Observer Report | 11 | Domicile: | {"VisibleLines":2,"Options":[{"Display":"BOS","Value":"BOS"},{"Display":"CLT","Value":"CLT"}]} |
| Observer Report | 11 | Fleet: | {"Options":[{"Display":"330","Value":"330"},{"Display":"320","Value":"320"}]} |
| Observer Report | 11 | Division: | {"Options":[{"Display":"Domestic","Value":"Domestic"},{"Display":"International","Value":"International"}]} |
| Observer Report | 11 | Are you on reserve? | {"Options":[{"Display":"Yes - Long Call Line","Value":"Yes - Long Call Line"},{"Display":"No","Value":"No"}]} |
Now from this returned result, for each Answer Question I need to parse the related Answer Options JSON data and extract the Value field for all the display attribute. So for example the JSON string in Answer Options for question "Are you on reserver?" looks like this:
"Options":[
{
"Display":"330",
"Value":"330",
"Selected":false
},
{
"Display":"320",
"Value":"320",
"Selected":false
},
{
"Display":"S80",
"Value":"S80",
"Selected":false
}
]
So I need to extract "Value" fields and return something like an array with values {330, 320, 195}.
In conclusion I want to construct a query where when I provide the Debrief Name and VersionNumber, it returns me the Answer Question and all the Answer Option values.
I am thinking of using a stored procedure like below:
CREATE PROCEDURE myProc
#DebriefName NVARCHAR(255),
#Version INT
AS
SELECT *
FROM [dbo].[myView]
WHERE [Debrief Name] = #DebriefName
AND Version = #Version
GO;
And then have another stored procedure that will capture this result from myProc and then do the JSON parsing:
CREATE PROCEDURE parseJSON
#DebriefName NVARCHAR(255),
#Version INT
AS
EXEC myProc #DebriefName, #Version; //Need to capture the result data in a temp table or something
// Parse the JSON data for each question item in temp table
GO;
I am not an expert in SQL so not sure how to do this. I read about Json parsing in SQL here and feel like I can use that but not sure how to in my context.
If you want to parse JSON data in Answer Options column and extract the Value field, you may try with the following approach, using OPENJSON() and STRING_AGG():
DECLARE #json nvarchar(max)
SET #json = N'{
"Options": [
{
"Display": "330",
"Value": "330",
"Selected": false
},
{
"Display": "320",
"Value": "320",
"Selected": false
},
{
"Display": "195",
"Value": "195",
"Selected": false
}
]
}'
SELECT STRING_AGG(x.[value], ', ') AS [Values]
FROM OPENJSON(#json, '$.Options') j
CROSS APPLY (SELECT * FROM OPENJSON(j.[value])) x
WHERE x.[key] = 'Value'
Output:
Values
330, 320, 195
If you want to build your statement using stored procedure, use this approach:
CREATE TABLE myTable (
DebriefName nvarchar(100),
Version int,
AnswerQuestion nvarchar(1000),
AnswerOptions nvarchar(max)
)
INSERT INTO myTable
(DebriefName, Version, AnswerQuestion, AnswerOptions)
VALUES
(N'Observer Report', 7, N'Division:' , N'{"Options":[{"Display":"Domestic","Value":"Domestic"},{"Display":"International","Value":"International"}]}'),
(N'Observer Report', 7, N'Are you on reserve?' , N'{"Options":[{"Display":"Yes - Long Call Line","Value":"Yes"},{"Display":"No","Value":"No"}]}'),
(N'Observer Report', 11, N'Crew Position:' , N'{"Options":[{"Display":"CA","Value":"CA"},{"Display":"RC","Value":"RC"},{"Display":"FO","Value":"FO"}]}'),
(N'Observer Report', 11, N'Domicile:' , N'{"VisibleLines":2,"Options":[{"Display":"BOS","Value":"BOS"},{"Display":"CLT","Value":"CLT"}]}'),
(N'Training Debrief', 12, N'TRAINING CREW POSITION', N'{"VisibleLines":2,"Options":[{"Display":"CA","Value":"CA"},{"Display":"FO","Value":"FO"}]}'),
(N'Training Debrief', 12, N'AIRCRAFT' , N'{"VisibleLines":2,"Options":[{"Display":"777","Value":"777"},{"Display":"767","Value":"767"}]}'),
(N'Security Debrief', 9, N'Aircraft Type' , N'{"Options":[{"Display":"MD-80","Value":"MD-80"},{"Display":"777","Value":"777"},{"Display":"767/757","Value":"767/757"}]}'),
(N'News Digest', 2, N'Do you read Digest?' , N'{"Options":[{"Display":"Yes","Value":"Yes"},{"Display":"No","Value":"No"}]}')
SELECT
t.AnswerQuestion,
STRING_AGG(x.[value], ', ') AS [Values]
FROM myTable t
CROSS APPLY (SELECT * FROM OPENJSON(t.AnswerOptions, '$.Options')) j
CROSS APPLY (SELECT * FROM OPENJSON(j.[value])) x
WHERE
DebriefName = N'Observer Report' AND
t.Version = 11 AND
x.[key] = 'Value'
GROUP BY
t.DebriefName,
t.Version,
t.AnswerQuestion
Output:
AnswerQuestion Values
Crew Position: CA, RC, FO
Domicile: BOS, CLT