Return SQL Server database query with nested Json - sql

I am trying to get this kind of answer when I consume my endpoint :
[
{
"McqID":"7EED5396-9151-4E3D-BCBF-FDB72CDD22B7",
"Questions":[
{
"QuestionId":"C8440686-531D-4099-89E9-014CAF9ED054",
"Question":"human text",
"Difficulty":3,
"Answers":[
{
"AnswerId":"7530DCF4-B2D9-48B0-9978-0E4690EA0C34",
"Answer":"human text2",
"IsTrue":false
},
{
"AnswerId":"5D16F17F-E205-42A5-873A-1A367924C182",
"Answer":"human text3",
"IsTrue":false
},
{
"AnswerId":"64E78326-77C3-4628-B9E3-2E8614D63632",
"Answer":"human text4",
"IsTrue":false
},
{
"AnswerId":"199241A9-0EF6-4F96-894A-9256B129CB1F",
"Answer":"human text5",
"IsTrue":true
},
{
"AnswerId":"EDCCAC18-5209-4457-95F2-C91666F8A916",
"Answer":"human text6",
"IsTrue":false
}
]
}
]
}
]
Here's my query (example) :
SELECT
Questions.QcmID AS QcmID,
(SELECT
Questions.id AS QuestionId,
Questions.Intitule AS Question,
Questions.Difficulte AS Difficulty,
(SELECT
Reponses.id AS AnswerId,
Reponses.Libelle AS Answer,
Reponses.IsTrue AS IsTrue
FROM
Reponses
WHERE
Reponses.QuestionID = Questions.id
FOR JSON PATH) AS Answers
FROM
Questions
WHERE
Questions.QcmID = '7EED5396-9151-4E3D-BCBF-FDB72CDD22B7'
FOR JSON PATH) AS Questions
FROM
Questions
WHERE
Questions.QcmID = '7EED5396-9151-4E3D-BCBF-FDB72CDD22B7'
FOR JSON PATH
I want a nested JSON representing my data, but it ends up being formatted like (smaller example) :
[
{
"JSON_F52E2B61-18A1-11d1-B105-00805F49916B":"[{\"QcmID\":\"7EED5396-9151-4E3D-BCBF-FDB72CDD22B7\"}]"
}
]
I've tried everything, FOR JSON PATH, FOR JSON AUTO, JSON_QUERY, etc...
Nothing works. FOR JSON PATH doesn't seem to work with multiple nested collections.
How do I get this result ?

You need to use JOINs as you would normally.
Using FOR JSON AUTO will pick the JOIN alias and if you want more control use the FOR JSON PATH.
I'm going to give you a generic example that will be easy to map to your scenario:
Option 1 - FOR JSON AUTO:
The JOIN alias will be used as the nested collection property name.
SELECT
ent.Id AS 'Id',
ent.Name AS 'Name',
ent.Age AS 'Age',
Emails.Id AS 'Id',
Emails.Email AS 'Email'
FROM Entities ent
LEFT JOIN EntitiesEmails Emails ON Emails.EntityId = ent.Id
FOR JSON AUTO
Option 2 - FOR JSON PATH:
You handle everything and note that the inner select must return a string, here also using FOR JSON PATH.
SELECT
ent.Id AS 'Id',
ent.Name AS 'Name',
ent.Age AS 'Age',
EMails = (
SELECT
Emails.Id AS 'Id',
Emails.Email AS 'Email'
FROM EntitiesEmails Emails WHERE Emails.EntityId = ent.Id
FOR JSON PATH
)
FROM Entities ent
FOR JSON PATH
Both generate the same result:
[{
"Id": 1,
"Name": "Alex",
"Age": 35,
"Emails": [{
"Id": 1,
"Email": "abc#domain.com"
}, {
"Id": 2,
"Email": "def#domain.com"
}, {
"Id": 3,
"Email": "ghi#domain.net"
}]
}, {
"Id": 2,
"Name": "Another Ale",
"Age": 40,
"Emails": [{
"Id": 4,
"Email": "user#skdfh.com"
}, {
"Id": 5,
"Email": "asldkj#als09q834.net"
}]
}, {
"Id": 3,
"Name": "John Doe",
"Age": 33,
"Emails": [{
"Id": 6,
"Email": "ooaoasdjj#ksjsk0913.org"
}]
}, {
"Id": 4,
"Name": "Mario",
"Age": 54,
"Emails": [{}]
}]
Cheers!

Related

Problem with using of FOR JSON AUTO in SQL Server

I am using FOR JSON AUTO in SQL server database, to convert my query's result to the JSON format.
in my query, I joined order table to two other tables.
SELECT
orders.[Code], orders.[Total], orders.[Discount],
customer.[Name], customer.[PhoneNumber],
store.[Name], store.[Address]
FROM
Orders orders
INNER JOIN
Customers customer ON (orders.[CustomerID] = customer.[ID])
INNER JOIN
Stores store ON (orders.[StoreID] = store.[ID])
FOR JSON AUTO
Result:
[
{
"Code": "1528",
"Total": 5000,
"Discount": 20,
"customer": [
{
"Name": "Alex",
"PhoneNumber": "(548) 123-5555",
"store": [
{
"Name": "Apple",
"Address": "E. Santa rd"
}
]
}
]
},
{
"Code": "1687",
"Total": 3000,
"Discount": 10,
"customer": [
{
"Name": "John",
"PhoneNumber": "(226) 354-7896",
"store": [
{
"Name": "Sony",
"Address": "W. Atlantic ave"
}
]
}
]
}
]
But it's not correct, because in this scenario customer and store are sibling and they have same parent, and both of them joined with the order table directly, correct JSON must be such as this:
[
{
"Code": "1528",
"Total": 5000,
"Discount": 20,
"customer": [
{
"Name": "Alex",
"PhoneNumber": "(548) 123-5555"
}
],
"store": [
{
"Name": "Apple",
"Address": "E. Santa rd"
}
]
},
{
"Code": "1687",
"Total": 3000,
"Discount": 10,
"customer": [
{
"Name": "John",
"PhoneNumber": "(226) 354-7896"
}
],
"store": [
{
"Name": "Sony",
"Address": "W. Atlantic ave"
}
]
}
]
how can I do that? Are there any option for this in SQL? (I don't want to use inner select.)
If there are one-to-one relationships between Orders and Customer and between Orders and Store then you can make the desired output by using PATH option and dot-separated column names:
SELECT
orders.[Code], orders.[Total], orders.[Discount],
customer.[Name] AS [Customer.Name], customer.[PhoneNumber] AS [Customer.PhoneNumber],
store.[Name] AS [Store.Name], store.[Address] AS [Store.Address]
FROM
Orders orders
INNER JOIN
Customers customer ON (orders.[CustomerID] = customer.[ID])
INNER JOIN
Stores store ON (orders.[StoreID] = store.[ID])
FOR JSON PATH
But if there are one-to-many relationships then you have to use nested queries:
SELECT
orders.[Code], orders.[Total], orders.[Discount],
(SELECT [Name], [PhoneNumber] FROM Customers WHERE Customers.ID=Orders.CustomerID FOR JSON AUTO) AS Customers,
(SELECT [Name], [Address] FROM Stores WHERE Stores.ID=Orders.StoreID FOR JSON AUTO) AS Stores
FROM
Orders orders
FOR JSON AUTO

Postgresql and jsonb - inserting a key/value into a multi-level array

Very similar to this post, but I struggle to adapt from their solution..
My table : public.challenge, column lines JSONB
My initial JSON in lines :
[
{
"line": 1,
"blocs": [
{
"size": 100,
"name": "abc"
},
{
"size": 100,
"name": "def"
},
{
"size": 100,
"name": "ghi"
}
]
},
{
"line": 2,
"blocs": [
{
"size": 100,
"name": "xyz"
}
]
}
]
Desired update :
[
{
"line": 1,
"blocs": [
{
"size": 100,
"name": "abc",
"type": "regular"
},
{
"size": 100,
"name": "def",
"type": "regular"
},
{
"size": 100,
"name": "ghi",
"type": "regular"
}
]
},
{
"line": 2,
"blocs": [
{
"size": 100,
"name": "xyz",
"type": "regular"
}
]
}
]
So basically I need to add the type key+value in every object of blocs, for each element of the root array.
My unsuccessful attempt looks like this :
UPDATE public.challenge SET lines = jsonb_set(lines, '{}', (
SELECT jsonb_set(line, '{blocs}', (
SELECT jsonb_agg( bloc || '{"type":"regular"}' )
FROM jsonb_array_elements(line->'{blocs}') bloc
))
FROM jsonb_array_elements(lines) line
))
;
(currently it sets the whole column as null, maybe due to jsonb_set(lines, '{}' while my json begins as an array ?)
Thanks!
Use jsonb_array_elements to unnest all the array elements and then add the required json and use jsonb_agg to aggregate it again:
with cte as
(select id,
jsonb_agg(jsonb_set(val1,
'{blocs}',
(select jsonb_agg(arr2 || '{"type": "regular"}')
from jsonb_array_elements(arr1.val1 - >
'blocs') arr2)))
from challenge,
jsonb_array_elements(lines) arr1(val1)
group by 1)
update challenge
set lines = (cte.jsonb_agg)
from cte
where challenge.id = cte.id
DEMO

How to get the inner elements of JsonB column in Postgres

I have a Json like this in my JsonB column:
{
"emails": [
{
"email": {
"id": "a8399412-165e-4601-824f-a55f631ad471",
"value": "test#gmail.com"
}
},
{
"email": {
"id": "fa09d9a7-a36a-42a4-8627-66b7554ce82e",
"value": "test1#gmail.com"
}
}
],
"Address": [
{
"address": {
"id": "a8399412-165e-4601-824f-a55f631ad471",
"addressLine1": "Line1"
}
},
{
"address": {
"id": "fa09d9a7-a36a-42a4-8627-66b7554ce82e",
"addressLine2": "Line2"
}
}
],
"lastName": {
"id": "bc10a5a9-04ff-4a00-b167-ac3232e5cb89",
"value": "LastName"
},
"firstName": {
"id": "4ccdd400-2586-4a7f-9379-aff4d1f5d9d6",
"value": "FirstName"
}
}
and so on. My requirement to get list of elements as key and value pairs with limit, I did a research tried different functions of postgres and I wrote the below query :
select response.* from my_table t, jsonb_each_text(jsonb_column) as response;
If I do like this I'm getting only the root elements like emails, firstName and lastName, but I want inner elements as well along with their values like below :
Key | value
------- ---------
"email" : {"id": "a8399412-165e-4601-824f-a55f631ad471","value": "test#gmail.com"}
"email" : {"id": "fa09d9a7-a36a-42a4-8627-66b7554ce82e","value": "test1#gmail.com"}
"lastName" : {"id": "bc10a5a9-04ff-4a00-b167-ac3232e5cb89","value": "LastName"}
"firstName" : {"id": "4ccdd400-2586-4a7f-9379-aff4d1f5d9d6","value": "FirstName"}
"address" : {"id": "a8399412-165e-4601-824f-a55f631ad471", "addressLine1": "Line1"}
"address" : {"id": "a8399412-165e-4601-824f-a55f631ad471", "addressLine2": "Line2"}
You can use jsonb_array_elements() function, and combine queries by UNION ALL
SELECT 'email' AS key, je.* ->> 'email' AS value
FROM my_table
CROSS JOIN jsonb_array_elements(jsonb_column->'emails') AS je
UNION ALL
SELECT 'address', ja.* ->> 'address'
FROM my_table
CROSS JOIN jsonb_array_elements(jsonb_column->'Address') AS ja
UNION ALL
SELECT 'lastName', (jsonb_column->'lastName')::text
FROM my_table
UNION ALL
SELECT 'firstName', (jsonb_column->'firstName' )::text
FROM my_table
Demo

SQL Query to get the count of Json Array

I have the below Json object. How do I get the count of Object Array.
{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC"
},
{
"ID": 11,
"Name": "ABCD"
},
]
}]}
I tried the below query but seems JSON_Length was not available.
SELECT ModelName,
JSON_LENGTH(JsonData, '$.Model[0].Object')
FROM TabA
The expected output should be
ModelName COUNT
Test Model 2
If you have valid JSON (at the moment you have a trailing comma (,_ after one of your closing braces (})), then you could use OPENJSON and COUNT:
DECLARE #JSON nvarchar(MAX) = N'{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC"
},
{
"ID": 11,
"Name": "ABCD"
}
]
}]}';
SELECT M.ModelName,
COUNT(O.[key]) AS [Count]
FROM (VALUES(#JSON))V(J)
CROSS APPLY OPENJSON(V.J)
WITH(ModelName varchar(20) '$.Model[0].ModelName',
[Object] nvarchar(MAX) '$.Model[0].Object' AS JSON) M
CROSS APPLY OPENJSON(M.[Object]) O
GROUP BY M.ModelName;

How to select field values from array of objects?

I have a JSON column with following JSON
{
"metadata": { "value": "JABC" },
"force": false,
"users": [
{ "id": "111", "comment": "abc" },
{ "id": "222", "comment": "abc" },
{ "id": "333" }
]
}
I am expecting list of IDs from the query output ["111","222", "333"]. I tried following query but getting null value.
select colName->'users'->>'id' ids from tableName
How to get this specific field value from the array of object?
You need to extract the array as rows and then get the id:
select json_array_elements(colName->'users')->>'id' ids from tableName;
If you're using jsonb rather than json, the function is jsonb_array_elements.