SQL json_object producing duplicate values - mysql

This is what i need to achieve:
{"id":1,
"work":[
{
"name":"Pied Piper3"
},
{
"name":"Pied Piper"
}
],
"awards":[
{
"title":"Digital Compression Pioneer Award"
}
]}
This is what i am getting
{"id":1,
"work":[
{
"name":"Pied Piper3"
},
{
"name":"Pied Piper"
}
],
"awards":[
{
"title":"Digital Compression Pioneer Award"
},
{
"title":"Digital Compression Pioneer Award"
}
]}
My query:
select json_object('id',basics.resume_id,
'work',JSON_ARRAYAGG(json_object('name', work.name)),
'awards', JSON_ARRAYAGG(JSON_OBJECT('title', awards.title))
) from basics
left join work on basics.resume_id = work.resume_id
left join awards on basics.resume_id = awards.resume_id where basics.resume_id = 1
I have added two rows of work for resume_id = 1 but only 1 row of awards for resume_id = 1.But I still get as many number of awards in result as per the number of works for resume_id =1

Related

How to join tables and get the json output using jooq

dslContext.select(
jsonObject(
key("id").value(CATEGORY.ID),
key("courses").value(
jsonArrayAgg(
jsonObject(
Arrays.stream(COURSE.fields())
.map(i -> key(CamelcaseConverter.snakeToCamel(i.getName())).value(
i))
.collect(
Collectors.toList())
)
)
)
)
).from(CATEGORY)
.leftJoin(COURSE_CATEGORY).on(CATEGORY.ID.eq(COURSE_CATEGORY.CATEGORY_ID))
.leftJoin(COURSE).on(COURSE.ID.eq(COURSE_CATEGORY.COURSE_ID)).fetchInto(JSONObject.class)
Output I got:
[
{
"courses": [
{
"id": 19
},
{
"id": null
}
],
"name": "Exam1",
"id": 1,
}
]
The required output is
[
{
"courses": [
{
"id": 19
}
],
"name": "Exam1",
"id": 1
},
{
"courses":[],
"name": "Exam2",
"id": 2
}
]
The query which need to be executed is
"select * from category left outer join course_category on category.id = course_category.category_id left outer join course on course_category.course_id = course.id"
how do I implement it?
You forgot to group by:
.groupBy(CATEGORY.ID, CATEGORY.NAME)
If you have a primary (or unique) key on CATEGORY.ID, then in MySQL, it will be sufficient to group by that
.groupBy(CATEGORY.ID)

How to return just distinct values from json.agg() function

I have the following query
select json_agg(t) from (
select json_build_object(
'field_1',a.field_1,
'field_2',a.field_2,
'field_4',json_agg(json_build_object('id',g.id,'data',g.fullname)),
'field_9',json_agg(json_build_object('id',i.optionid,'data',i.option))
) as data
from schema_1.tbl_342 a
left join schema_1.tbl_342_to_tbl_329_field_10 b on a.id=b.tbl_342_id
left join schema_1.tbl_329_customid c on b.tbl_329_id=c.id
left join schema_1.tbl_329_field_23_join d on c.id=d.id
left join schema_1.tbl_329_field_23 e on d.optionid = e.optionid
left join schema_1.tbl_342_to_tbl_312_field_4 f on a.id=f.tbl_342_id
left join schema_1.tbl_312_customid g on f.tbl_312_id = g.id
left join schema_1.tbl_342_field_9_join h on h.id=a.id
left join schema_1.tbl_342_field_9 i on i.optionid=h.optionid
group by a.field_1,a.field_2
) t
This results in the following JSON format
[
{
"data":{
"field_1":"John",
"field_2":null,
"field_4":[
{
"id":null,
"data":null
}
],
"field_9":[
{
"id":2,
"data":"Green"
}
]
}
},
{
"data":{
"field_1":"Jackson",
"field_2":null,
"field_4":[
{
"id":2,
"data":"Marketing Manager M1004"
},
{
"id":4,
"data":"Senior Javascript Engineer"
},
{
"id":5,
"data":"Recruiter"
}
],
"field_9":[
{
"id":3,
"data":"Red"
},
{
"id":3,
"data":"Red"
},
{
"id":3,
"data":"Red"
}
]
}
},
{
"data":{
"field_1":"Jacob",
"field_2":null,
"field_4":[
{
"id":null,
"data":null
}
],
"field_9":[
{
"id":null,
"data":null
}
]
}
},
{
"data":{
"field_1":"Todd",
"field_2":null,
"field_4":[
{
"id":null,
"data":null
}
],
"field_9":[
{
"id":4,
"data":"Yellow"
}
]
}
},
{
"data":{
"field_1":"Billy",
"field_2":null,
"field_4":[
{
"id":5,
"data":"Recruiter"
}
],
"field_9":[
{
"id":1,
"data":"Blue"
}
]
}
}
]
In this example, I'm trying to fix two things.
1.Removing the data node element. I'd like to have the objects start at root, not in Data
2.Notice in the second node. The field_4 has 3 elements, but field_9 has a single red value in DB. Here it's repeating the duplicate values, must be matching the number of returned records from field_4
How can I get just distinct values from this aggregate?
I've tried
'field_4',distinct json_agg(json_build_object('id',g.id,'data',g.fullname)),
and other forms like that, but it doesn't like the syntax.
FYI, I'm using PostgreSQL 11
Answers and solution for your problem will be as below:
Problem 1 - You are using json_agg(t) to generate the final JSON data which will pick all column names or alias as key for JSON and aggregate it. So here you should use array_to_json(array_agg(t.data)). array_agg will convert the result of subquery in array and then array_to_json will convert the final array to JSON.
Problem 2 - You want distinct json object in inner array. So Distinct can not be used with json data type because there is no equality operator available in PostgreSQL for JSON type. So you should use JSONB.
Considering your query mentioned in the question is working fine, try it :
select array_to_json(array_agg(t.data)) from (
select jsonb_build_object(
'field_1',a.field_1,
'field_2',a.field_2,
'field_4',jsonb_agg(distinct jsonb_build_object('id',g.id,'data',g.fullname)),
'field_9',jsonb_agg(distinct jsonb_build_object('id',i.optionid,'data',i.option))
) as data
from schema_1.tbl_342 a
left join schema_1.tbl_342_to_tbl_329_field_10 b on a.id=b.tbl_342_id
left join schema_1.tbl_329_customid c on b.tbl_329_id=c.id
left join schema_1.tbl_329_field_23_join d on c.id=d.id
left join schema_1.tbl_329_field_23 e on d.optionid = e.optionid
left join schema_1.tbl_342_to_tbl_312_field_4 f on a.id=f.tbl_342_id
left join schema_1.tbl_312_customid g on f.tbl_312_id = g.id
left join schema_1.tbl_342_field_9_join h on h.id=a.id
left join schema_1.tbl_342_field_9 i on i.optionid=h.optionid
group by a.field_1,a.field_2
) t

Parsing JSON in Postgres

I have the following JSON that I'd like to parse inside a postgresql function.
{
"people": [
{
"person_name": "Person#1",
"jobs": [
{
"job_title": "Job#1"
},
{
"job_name": "Job#2"
}
]
}
]
}
I need to know how to pull out the person_name, and then loop thru the jobs and pull out the job_title. This is as far as I've been able to get.
select ('{"people":[{"person_name":"Person#1","jobs":[{"job_title":"Job#1"},
{"job_name":"Job#2"}]}]}')::json -> 'people';
https://www.db-fiddle.com/f/vcgya7WtVdvj8q5ck5TqgX/0
Assuming that job_name in your post should be job_title. I expanded your test data to:
{
"people": [{
"person_name": "Person#1",
"jobs": [{
"job_title": "Job#11"
},
{
"job_title": "Job#12"
}]
},
{
"person_name": "Person#2",
"jobs": [{
"job_title": "Job#21"
},
{
"job_title": "Job#22"
},
{
"job_title": "Job#23"
}]
}]
}
Query:
SELECT
person -> 'person_name' as person_name, -- B
json_array_elements(person -> 'jobs') -> 'job_title' as job_title -- C
FROM (
SELECT
json_array_elements(json_data -> 'people') as person -- A
FROM (
SELECT (
'{"people":[ '
|| '{"person_name":"Person#1","jobs":[{"job_title":"Job#11"}, {"job_title":"Job#12"}]}, '
|| '{"person_name":"Person#2","jobs":[{"job_title":"Job#21"}, {"job_title":"Job#22"}, {"job_title":"Job#23"}]} '
|| ']}'
)::json as json_data
)s
)s
A Getting person array; json_array_elements expands all array elements into one row per element
B Getting person_name from array elements
C Expanding the job array elements into one row per element and getting the job_title
Result:
person_name job_title
----------- ---------
"Person#1" "Job#11"
"Person#1" "Job#12"
"Person#2" "Job#21"
"Person#2" "Job#22"
"Person#2" "Job#23"

N1ql Queries for traversing array of objects

Below is couchbase document structure I am working on :
{
"name":"Harry",
"lastname":"sam",
"supplier_info": {
"HU": [
{
"40383": "Bangalore."
},{
"41163": "new."
}
],
"SK": [
{
"40383": "DYNAMIT KFT."
}
]
}
Requirement is to get all the documents from my db which has supplier number as "40383" irrespective of country. How can I do that with N1ql queries? (I do not have country info as well)
SELECT * FROM default WHERE ANY v IN ARRAY_FLATTEN(OBJECT_VALUES(supplier_info),2) SATISFIES "40383" IN OBJECT_NAMES(v) END;
Input data
INSERT INTO default VALUES("kkk01",{ "supplier_info": { "HU": [ { "40383": "Bangalore." },{ "41163": "new." } ], "SK": [ { "40383": "DYNAMIT KFT." } ] }}), VALUES("kkk03",{ "supplier_info": { "HU": [ { "40383": "Bangalore." },{ "41164": "az." } ], "SK": [ { "40385": "DYNAMIT" } ] }});
The following query gives Distinct countries
SELECT DISTINCT country FROM (SELECT RAW ARRAY_FLATTEN(ARRAY ARRAY v.name FOR u IN v.val WHEN "40383" IN OBJECT_NAMES(u) END FOR v IN OBJECT_PAIRS(supplier_info) END,2) FROM default ) AS countries UNNEST countries AS country WHERE country IS NOT MISSING;
Getting supplier name, country, name and lastname for all the records with supplier number 40383.
SELECT q.name, q.lastname, s.country, s.snumber, s.sname FROM (SELECT name, lastname, ARRAY_FLATTEN(ARRAY ARRAY {"country":v.name, "snumber":OBJECT_PAIRS(u)[0].name, "sname":OBJECT_PAIRS(u)[0].val} FOR u IN v.val WHEN "40383" IN OBJECT_NAMES(u) END FOR v IN OBJECT_PAIRS(supplier_info) END,2) AS sinfo FROM default ) AS q UNNEST q.sinfo AS s;

Couchbase N1QL array query

Document sample from my giata_properties bucket: link
Relevant json paste
{
"propertyCodes": {
"provider": [
{
"code": [
{
"value": [
{
"value": "304387"
}
]
}
],
"providerCode": "hotelbeds",
"providerType": "gds"
},
{
"code": [
{
"value": [
{
"name": "Country Code",
"value": "EG"
},
{
"name": "City Code",
"value": "HRG"
},
{
"name": "Hotel Code",
"value": "91U"
}
]
}
],
"providerCode": "gta",
"providerType": "gds"
}
]
},
"name": "Arabia Azur Resort"
}
I want a query (and an index) to retrieve a document based on propertyCodes.provider.code.value.value and propertyCodes.provider.providerCode. I've managed to do each separately but I'm not sure how to merge both of them in a single query.
SELECT meta().id FROM giata_properties AS gp USE INDEX(`#primary`) WHERE ANY v WITHIN gp.propertyCodes.provider[*].code SATISFIES v.`value` = '150613' END;
SELECT meta().id FROM giata_properties AS gp USE INDEX(`#primary`) WHERE ANY v within gp.propertyCodes.provider[*].providerCode SATISFIES v = 'hotelbeds' END;
So for example I want to fetch the document that includes propertyCodes.provider.code.value.value of 304387 and that provider is also hotelbeds, because code value can be duplicated over documents, but code and providerCode combination is unique.
Here are the query and the indexes.
The query.
SELECT META().id
FROM giata_properties AS gp
WHERE ANY p IN propertyCodes.provider SATISFIES ( ANY v WITHIN p.code SATISFIES v.`value` = '304387' END ) AND p.providerCode = 'hotelbeds' END;
The indexes.
CREATE INDEX idx_value ON giata_properties
( DISTINCT ARRAY ( DISTINCT ARRAY v.`value` FOR v WITHIN p.code END ) FOR p IN propertyCodes.provider END );
CREATE INDEX idx_providerCode ON giata_properties
( DISTINCT ARRAY p.providerCode FOR p IN propertyCodes.provider END );