How to use a JSON subquery in a MySQL JSON Statement - mysql

Following scenario: I have got a JSON data column named 'address' in my sql table. I inserted an array of objects into it. Later I will need to pick a whole object of the array by finding out which type in the request will be passed.
So if I pass a { "type": "shipping" } to my REST API I would like to get all the data in the object with the type "shipping" marked.
Just one of the different JSON formats I tried:
{
"address": [
{
"type": "shipping",
"street": "streetName",
"streetNumber": "1"
},
{
"type": "billing",
"street": "streetName",
"streetNumber": "2"
},
{
"type": "custom",
"street": "streetName",
"streetNumber": "3"
}
]
}
This is how the data gets stored in the single column:
[
{"type": "custom", "street": "streetName", "streetNumber": "1"},
{"type": "shipping", "street": "streetName", "streetNumber": "2"},
{"type": "billing", "street": "streetName", "streetNumber": "3"}
]
So I tried to do some magic with the JSON function of MySQL. Querys like
SELECT JSON_SEARCH(address, 'one', 'shipping')
FROM user_data;
works acutally fine but it is not the result I need. So I thought about using subquerys
SELECT JSON_EXTRACT(address, (SELECT JSON_SEARCH(address, 'one', 'shipping')))
FROM user_data
WHERE user_id = 1;
but this just ends up with "invalid JSON path expression" (Error Code: 3143).
I already tried with different querys and JSON data formats like instead using an array I tried to insert the data like
"address": {
"shipping": {
"street": ...
.
},
"billing": {
"street": ...
.
}
}
Now I am just getting confused and it seems like there is just the mysql documentation which does not help right now...

Your query generate invalid JSON path expression because the result contains quotes
SELECT JSON_SEARCH(address, 'one', 'shipping')
FROM user_data;
Result: "$[0].type"
You can strip those quotes with REPLACE function
SELECT REPLACE( JSON_SEARCH(address, 'one', 'shipping'), '"', '')
FROM user_data;
Result: $[0].type
To get the object path, you should replace also '.type' from the end of the result
SELECT REPLACE(REPLACE( JSON_SEARCH(address, 'one', 'shipping'), '"', ''), '.type', '')
FROM user_data;
Result: $[0]
Final query looks like this:
SELECT
JSON_EXTRACT(
address,
REPLACE(
REPLACE(
(SELECT JSON_SEARCH(address, 'one', 'billing') FROM user_data),
'"',
'' ),
'.type',
'')
)
FROM user_data
WHERE user_id = 1;
Example here: https://www.db-fiddle.com/f/mYBXEs3M4xFDGeQvGwUgqQ/0

Related

Can't get access to data from nested json's array

How to retrieve values from employment_types (type, salary) and skills (name, level) arrays and show them in columns? I tried with employment_types and it doesn't work not to mention skills:
declare #json nvarchar(max)
set #json = '[
{
"title": "IT Admin",
"experience_level": "mid",
"employment_types": [
{
"type": "permanent",
"salary": null
}
],
"skills": [
{
"name": "Security",
"level": 3
},
{
"name": "WIFI",
"level": 3
},
{
"name": "switching",
"level": 3
}
]
},
{
"title": "Lead QA Engineer",
"experience_level": "mid",
"employment_types": [
{
"type": "permanent",
"salary": {
"from": 7000,
"to": 13000,
"currency": "pln"
}
}
],
"skills": [
{
"name": "Embedded C",
"level": 4
},
{
"name": "Quality Assurance",
"level": 4
},
{
"name": "C++",
"level": 4
}
]
}
]';
SELECT *
FROM OPENJSON(#JSON, '$.employment_types')
WITH
(
type nvarchar(50) '$.type',
salary varchar(max) '$.salary'
)
There are almost 7000 records and I'd like to show mentioned above columns from all of them.
It's hard to know exactly what you want, given that both employment_types and skills are arrays. But assuming employment_types always has only one element, you could do something like this
SELECT
j1.title,
j1.experience_level,
j1.employment_type,
salary = j1.salary_currency + ' ' + CONCAT(j1.salary_from, ' - ', j1.salary_to),
j2.name,
j2.level
FROM OPENJSON(#JSON)
WITH (
title nvarchar(100),
experience_level nvarchar(10),
employment_type nvarchar(50) '$.employment_types[0].type',
salary_from int '$.employment_types[0].salary.from',
salary_to int '$.employment_types[0].salary.to',
salary_currency char(3) '$.employment_types[0].salary.currency',
skills nvarchar(max) AS JSON
) j1
CROSS APPLY OPENJSON(j1.skills)
WITH
(
name nvarchar(50),
level int
) j2
db<>fiddle
Since we are pulling data directly from the root object, we don't need a JSON path argument. OPENJSON will automatically break out an array into separate rows. If you just wanted employment_types, you could go directly to that with a path argument.
employment_types[0] means to only get the first element of the array. If you want all the elements, you will need another OPENJSON
Note the use of AS JSON for skills, this means that the entire JSON array is pulled out, and can then be pushed through another call to OPENJSON

Retrieving nested json information from a postgresql table - no current index

I'd like to retrieve nested json information from a jsonb field (sub_table) in a postgresql table (prices).
I'm able to retrieve the json using the command:
with jsontable as (
SELECT "sub_table"
FROM "prices"
WHERE "Scenario" = 'A' AND "data_type" = 'new'
)
SELECT * from jsontable
This returns a json table like this:
{
"0": {
"Name": "CompX",
"Price": 10,
"index": 1,
"Date": "2020-01-09T00:00:00.000Z"
},
"1": {
"Name": "CompY",
"Price": 20,
"index": 1,
"Date": "2020-01-09T00:00:00.000Z"
},
"2": {
"Name": "CompX",
"Price": 19,
"index": 2,
"Date": "2020-01-10T00:00:00.000Z"
}
}
I want to return all data relating to Name = "CompX" but can't get the query to work.
I've tried to follow the examples here, but can't work it out. Do I need to reindex in some way?
You can return extracted JSONB data row-wisely by using jsonb_each function with filtering out by (j.value -> 'Name')::text = '"CompX"' condition :
SELECT j.value
FROM prices p
CROSS JOIN jsonb_each(sub_table) AS j(e)
WHERE (j.value -> 'Name')::text = '"CompX"'
Demo

How to properly unpack a JSON array in SQL Server

I'm making a foray into JSON, I'd like to add a user to multiple groups: insert an JSON array into a table.
Ideally, the JSON would look like this:
'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Groups": [{"0", "1", "5"}]
"Better_Groups": [{"ID":"0", "ID":"1", "ID":"5"}]
}'
Currently, I can do it with JSON like this:
'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Groups": "1,2,3,4"
}'
then "unpack" it with the following ditty:
declare #groups varchar(1000)
select #groups = Groups from openjson(#json)
WITH
(
Groups nvarchar(100) '$.Groups'
)
print #groups
select value from string_split(#groups, ',')
which returns a nice little table like so:
Value
1
2
3
4
Problem This is bad JSON and the Web developer will make fun of me.
Question How do you propely unpack a JSON array in SQL Server?
update:
The final JSON used looks like so:
#json =
'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Groups": "1,2,3,4",
"Better_Groups": ["0", "1", "5"]
}'
Assuming that "groups" element is an array:
DECLARE #json NVARCHAR(MAX) =
N'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Better_Groups": ["0", "1", "5"]
}';
SELECT s.value
FROM OPENJSON(JSON_QUERY(#json, '$.Better_Groups')) s;
db<>fiddle demo

Add an element at the beginning of a JSON using JSON_MODIFY

I am trying to build a JSON from the following table
name | flag
------+------
foo | fail
bar | pass
using the query,
DECLARE #JSONDATA nvarchar(MAX) = (SELECT [name], [flag]
FROM test
FOR JSON AUTO, ROOT('students'))
SET #JSONDATA = JSON_MODIFY(#JSONDATA, '$.class','10')
The generated JSON here is
{
"students": [
{
"name": "foo",
"flag": "fail"
},
{
"name": "bar",
"flag": "pass"
}
],
"class": "10"
}
I need to the class element at the very first node of the JSON. Is there any way, using JSON_MODIFY ?
Fiddle
At a loss forcing a sequence via modify.
Perhaps an alternative
Select class=10
,students = (SELECT [name], [flag] FROM test FOR JSON AUTO)
For JSON path, without_array_wrapper
Returns
{
"class": 10,
"students": [{
"name": "foo",
"flag": "fail"
}, {
"name": "bar",
"flag": "pass"
}]
}
EDIT- Updated SELECT as suggested by GSerg

select values in json into other json (Postgres)

I have the following JSON
{
"eventSummaryList": [
{
"customer": "189256",
"data": "{\"cliente\":\"189256\",\"data_posicao\":\"1491426372\",\"gps_valido\":\"1\",\"horimetro\":\"120561\",\"ibuttonHex\":\"0\",\"ibuttonPart1\":\"0\",\"ibuttonPart2\":\"0\",\"id_evento\":\"null\",\"id_motorista\":\"0\",\"ignicao\":\"1\",\"latitude\":\"-2222222\",\"longitude\":\"-2222222\",\"odometro\":\"253692\",\"pos_memoria\":\"0\",\"veiculo\":\"44444\",\"velocidade\":\"50\"}",
"identifierEventRule": "77404",
"identifierRule": "6",
"identifierSummary": "28901976",
"rule": "velocidade_maior_que",
"status": 1,
"vehicle": "44444"
}
],
"header": {
"mensagem": {
"estilo": "SUCCESS",
"mensagem": "Successfully executed service",
"plataforma": "EVENT_POINT",
"status": "SUCESSO"
}
}
}
And I need to extract the value "velocidade" what's inside "data" that
contains another json.
I'm using the following syntax but it returns null.
select cast((value::json ->'data')::json->> 'velocidade' AS int) AS velocidade,
Try:
SELECT ((value::json #>> '{eventSummaryList,0,data}')::json ->> 'velocidade')::int
(The #>> or ->> operators are the key. If you use #> or ->, you'll end up with a json-encoded json string. BTW this is really a messed up model, I would look into fixing its input first/instead.)
http://rextester.com/THVYFK9026