Nested JSON using ms sql server - sql

I have a table with USER_DATA (user data table) with 2 rows (2 entries basically)
I created one nested JSON query ->
SELECT CONCAT( first_name,' ', last_name) AS displayName,
first_name AS givenName, last_name AS surname,
identities = (SELECT login_name AS issuerAssignedId
FROM user_data
FOR JSON AUTO)
FROM user_data
FOR JSON PATH, ROOT('users');
Here, I am getting this output ->
{
"users": [
{
"displayName": "David Dave",
"givenName": "David",
"surname": "Dave",
"identities": [
{
"issuerAssignedId": "System"
},
{
"issuerAssignedId": "Administrators"
}
]
},
{
"displayName": "Tony Padila",
"givenName": "Tony",
"surname": "Padila",
"identities": [
{
"issuerAssignedId": "System"
},
{
"issuerAssignedId": "Administrators"
}
]
}
But the problem is -> inside identities,
"issuerAssignedId": "System" ----> Belongs to Dave
"issuerAssignedId": "Administrators" ----> Belongs to Tony
But I am not able to stop the inner select query (Not able to map correctly)
The correct output should be --->
{
"users": [
{
"displayName": "David Dave",
"givenName": "David",
"surname": "Dave",
"identities": [
{
"issuerAssignedId": "System"
}
]
},
{
"displayName": "Tony Padila",
"givenName": "Tony",
"surname": "Padila",
"identities": [
{
"issuerAssignedId": "Administrators"
}
]
}
PLEASE HELP.

You are missing the condition in the inner query and why do you want the identities to be a separate array in the JSON output.
I have updated the query as per my understanding please refer below sql, I'm not sure why you are having the ** in the query
SELECT CONCAT (
FIRST_NAME
,' '
,LAST_NAME
) AS displayName
,FIRST_NAME AS givenName
,LAST_NAME AS surname
,identities = (
SELECT innr.LOGIN_NAME AS issuerAssignedId
FROM USER_DATA
innr
WHERE
innr.LOGIN_NAME = ottr.LOGIN_NAME
FOR JSON AUTO
)
FROM USER_DATA ottr
FOR JSON PATH
,ROOT('users');

Related

How to update multiple occurrence a specific value of a object present in array of object within Postgres JSON Field

Here is my JSON field where has multiple users with the same name. I want to update all users whose name is Devang to Dev
JSON
{
"user": [
{
"user_name": "Devang",
"user_weight": 0.7676846955248864
},
{
"user_name": "Meet",
"user_weight": 0.07447325861051013
},
{
"user_name": "Devang",
"user_weight": 0.056163873153859706
}
],
"address": [
{
"address_name": "India"
}
]
}
After Update The JSON would be
{
"user": [
{
"user_name": "Dev",
"user_weight": 0.7676846955248864
},
{
"user_name": "Meet",
"user_weight": 0.07447325861051013
},
{
"user_name": "Dev",
"user_weight": 0.056163873153859706
}
],
"address": [
{
"address_name": "India"
}
]
}
Here I have tried this query but update only the first occurrence due to subquery.
with cte as (
select id, ('{user,'||index-1||',user_name}')::text[] as json_path
from user_table, jsonb_array_elements(json_field->'user')
with ordinality arr(vals,index) where arr.vals->>'user_name' ='Devang'
)
update user_table
set json_field = jsonb_set(json_field,cte.json_path,'"Dev"',false)
from cte where user_table.id=cte.id;
Please also look at this DEMO
Any answer will be appreciated
You may use string function REPLACE:
UPDATE user_table
SET json_field = REPLACE(json_field :: TEXT, '"user_name": "Devang"', '"user_name": "Dev"') :: JSONB;
https://dbfiddle.uk/?rdbms=postgres_10&fiddle=fa36275977f85a1233bcbec150ada266

How to replace a string value with an object inside a column the contains json object

I'm using PostgreSQL and I have a table that has a text column named additional_info.
This columns on this table currently contains a JSON object as below:
{
"dbServiceAccount": {
"aggregationMode": "DbServiceAccount",
"destinationIp": "10.10.10.29",
"db": "xe",
"dbType": "Oracle",
"dbUser": "system"
},
"clients": [{
"user": "user5"
}, {
"user": "user4"
}]
}
I want to replace the value of the key 'dbServiceAccount' with an object.
The object should have a key 'name' that holds the original string value, like below:
{
"dbServiceAccount": {
"aggregationMode": {
"name": "DbServiceAccount"
},
"destinationIp": "10.10.10.29",
"db": "xe",
"dbType": "Oracle",
"dbUser": "system"
},
"clients": [{
"user": "user5"
}, {
"user": "user4"
}]
}
How can I do this?
This can be done using jsonb_set.
with t(oldj) as
(
select '{
"dbServiceAccount": {
"aggregationMode": "DbServiceAccount",
"destinationIp": "10.10.10.29",
"db": "xe",
"dbType": "Oracle",
"dbUser": "system"
},
"clients": [{"user": "user5"}, {"user": "user4"}]
}'::jsonb
) -- the original JSONB object
select jsonb_set
(
oldj,
array['dbServiceAccount', 'aggregationMode'],
jsonb_build_object('name', oldj -> 'dbServiceAccount' ->> 'aggregationMode')
) as newj -- the resulting JSONB object
from t;
/* result:
{
"clients": [
{
"user": "user5"
},
{
"user": "user4"
}
],
"dbServiceAccount": {
"db": "xe",
"dbType": "Oracle",
"dbUser": "system",
"destinationIp": "10.10.10.29",
"aggregationMode": {
"name": "DbServiceAccount"
}
}
}
*/
I finally solved this:
WITH aggregationMode AS (
select additional_info::json ->'dbServiceAccount' -> 'aggregationMode' as aggregationMode from incidents."groups" g where id in ('3085875798')
)
UPDATE
incidents."groups" g
SET
additional_info = REPLACE(additional_info,CONCAT ('"aggregationMode":"', (select * from aggregationMode), '"'),
CONCAT ('"aggregationMode": {"name": "', (select * from aggregationMode), '"}'))
where id = '111'

Oracle JSON query

I have a JSON column which on persons table which stores the best activities people have done in countries they visited like this...
{
"countries": [
{
"name": "Ireland",
"bestActivity": {
"name": "Drinking"
}
},
{
"name": "Scotland",
"bestActivity": {
"name": "Dancing"
}
}
]
}
Someone else might be:
{
"countries": [
{
"name": "Ireland",
"bestActivity": {
"name": "Football"
}
},
{
"name": "England",
"bestActivity": {
"name": "Golf"
}
}
]
}
I want to select all the people who visited Ireland and with best activity of Drinking in Ireland and also visited Scotland and with the best activity of Drinking.
Really struggling. Any tips?
You can use the JSON_EXISTS function to filter your table based on the criteria you described. I have built a sample query below using the JSON structures you provided in your question.
WITH
sample_table (first_name, json_col)
AS
(SELECT 'John', '{
"countries": [
{
"name": "Ireland",
"bestActivity" : {
"name": "Drinking"
},
},
{
"name": "Scotland",
"bestActivity" : {
"name": "Dancing"
}
}
}' FROM DUAL
UNION ALL
SELECT 'Jane', '{
"countries": [
{
"name": "Ireland",
"bestActivity" : {
"name": "Football"
},
},
{
"name": "England",
"bestActivity" : {
"name": "Golf"
}
}
}' FROM DUAL)
SELECT *
FROM sample_table s
WHERE JSON_EXISTS (s.json_col, '$.countries[*]?(#.name == "Scotland")')
AND JSON_EXISTS (
s.json_col,
'$.countries[*]?(#.name == "Ireland" && #.bestActivity.name == "Drinking")');
You can use JSON_TABLE() function as your database version is 12c(for all subversions) such as
SELECT first_name
FROM t,
JSON_TABLE(jscol, '$.countries[*]'
COLUMNS (
country VARCHAR2(90) PATH '$.name',
bestActivity VARCHAR2(90) PATH '$.bestActivity.name'
)
)
WHERE (country = 'Ireland' AND bestActivity = 'Drinking')
OR country = 'Scotland'
GROUP BY first_name
HAVING COUNT(*)>1
Demo
You can use JSON_EXISTS() function is database version is 12cR2+ in such a format
SELECT first_name
FROM t
WHERE JSON_EXISTS(jscol,
'$.countries?(#.name == "Ireland"
&& #.bestActivity.name == "Drinking")')
AND JSON_EXISTS(jscol,
'$.countries?(#.name == "Scotland")');
Demo
Notice that the query should be invoked after issuing SET DEFINE OFF when SQL*Plus is used in order to prevent the && operands to be interpreted as a substitution variable.

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

Return SQL Server database query with nested Json

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!