SQL Server JSON output of nested tables - sql

I have the following tables:
create table students
(
id int,
name varchar(10)
)
create table subjects
(
subjectId int,
studentId int,
subject varchar(12)
)
create table marks
(
studentId int,
subjectId int,
marks int
)
create table sports
(
sportId int,
studentId int,
name varchar(12)
)
with the following data:
insert into students values(1, 'Rusty');
insert into subjects values(1, 1, 'math')
insert into subjects values(2, 1, 'science')
insert into marks values(1,1,50)
insert into marks values(1,2,60)
insert into sports values(1, 1, 'soccer')
insert into sports values(2, 1, 'baseball')
I want to write a query in SQL Server to get the following output:
studentId = 1
{
"id": 1,
"name": "Rusty",
"subjects" : [
{
"name": "math",
"marks": 50
},
{
"name": "science",
"marks": 60
}
],
"sports": [
{
"name": "soccer"
},
{
"name": "baseball"
}
]
}
I tried the following query
select *
from students s
join subjects su on (s.id = su.studentId)
join sports sp on (s.id = sp.studentId)
where s.id = 1
for json auto
and here is the output:
[
{
"id": 1,
"name": "Rusty",
"su": [
{
"subjectId": 1,
"studentId": 1,
"subject": "math",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "soccer"
}
]
},
{
"subjectId": 1,
"studentId": 1,
"subject": "science",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "soccer"
}
]
},
{
"subjectId": 1,
"studentId": 1,
"subject": "math",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "baseball"
}
]
},
{
"subjectId": 1,
"studentId": 1,
"subject": "science",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "baseball"
}
]
}
]
}
]

For your desired output you can use correlated subqueries for sports and subjects that generate their own JSON, using FOR JSON PATH, and include that information as an array of nested objects in your main JSON output by way of JSON_QUERY (Transact-SQL), e.g.:
/*
* Data setup...
*/
create table students (
id int,
name varchar(10)
);
create table subjects (
subjectId int,
studentId int,
subject varchar(12)
);
create table marks (
studentId int,
subjectId int,
marks int
);
create table sports (
sportId int,
studentId int,
name varchar(12)
);
insert into students (id, name) values
(1, 'Rusty');
insert into subjects (subjectId, studentId, subject) values
(1, 1, 'math'),
(2, 1, 'science');
insert into marks (studentId, subjectId, marks) values
(1,1,50),
(1,2,60);
insert into sports (sportId, studentId, name) values
(1, 1, 'soccer'),
(2, 1, 'baseball');
/*
* Example query...
*/
select
students.id,
students.name,
json_query(( --<<-- doubled brakcets
select
subjects.subject,
marks.marks
from subjects
join marks
on marks.subjectId = subjects.subjectId
and marks.studentId = subjects.studentId
where subjects.studentId = students.id
for json path
)) as [subjects],
json_query(( --<<-- doubled brackets
select
sports.name
from sports
where sports.studentId = students.id
for json path
)) as [sports]
from students
where students.id = 1
for json path, without_array_wrapper;
Which yields the JSON output:
{
"id": 1,
"name": "Rusty",
"subjects": [
{
"subject": "math",
"marks": 50
},
{
"subject": "science",
"marks": 60
}
],
"sports": [
{
"name": "soccer"
},
{
"name": "baseball"
}
]
}

Related

SQL- Merging json with a node name

I have a table with json vaules in nvarchar columns
ID ConfigKey ConfigValue
1 employee {"name":"jhon","age":"23","salary":"5000"}
2 Dept {"name": "Marketing", "code":"12", "manager":"sam"}
3 manager {"managername":"abc", "dept":"AB"}
I need to get merged json for selected ConfiKey by adding ConfigKey value as node name for the ConfigValue and also merged json as below.
For example when i want to get for employee and manager. Output should be like
{
"employee": {
"name": "jhon",
"age": "23",
"salary": "5000"
},
"manager": {
"managername": "abc",
"dept": "AB"
}
}
When I want to get for only ConfigKey value employee output should be
{
"employee": {
"name": "jhon",
"age": "23",
"salary": "5000"
}
}
I tried using JSON_MODIFY but couldn't achieve this. How I can achieve this in SQL query.
You can do this with PIVOT and JSON_QUERY but the code will quickly get messy as you add more ConfigKey rows...
/*
* Setup example type, table and data...
*/
create type ConfigKeyTableType as table (
ConfigKey nvarchar(10)
);
create table dbo.Example (
ID int,
ConfigKey nvarchar(10),
ConfigValue nvarchar(max)
);
insert dbo.Example (ID, ConfigKey, ConfigValue) values
(1, N'employee', N'{"name":"jhon","age":"23","salary":"5000"}'),
(2, N'Dept', N'{"name": "Marketing", "code":"12", "manager":"sam"}'),
(3, N'manager', N'{"managername":"abc", "dept":"AB"}');
go
/*
* Stored procedure to return the JSON based on the supplied ConfigKey values...
*/
create procedure dbo.usp_GetConfigJSON (
#ConfigKeys ConfigKeyTableType readonly
)
as
begin
set nocount on;
select
json_query(dept) as [dept],
json_query(employee) as [employee],
json_query(manager) as [manager]
from (
select Ex.ConfigKey, Ex.ConfigValue
from dbo.Example Ex
join #ConfigKeys keys on keys.ConfigKey=Ex.ConfigKey
) src
pivot (max(ConfigValue) for ConfigKey in ([dept], [employee], [manager])) pvt
for json auto, without_array_wrapper;
end
go
To invoke it from SQL...
declare #ConfigKeys ConfigKeyTableType;
insert #ConfigKeys (ConfigKey) values
(N'employee'),
(N'manager');
exec dbo.usp_GetConfigJSON #ConfigKeys;
go
declare #ConfigKeys ConfigKeyTableType;
insert #ConfigKeys (ConfigKey) values
(N'employee');
exec dbo.usp_GetConfigJSON #ConfigKeys;
go
Which yields the results...
{
"employee": {
"name": "jhon",
"age": "23",
"salary": "5000"
},
"manager": {
"managername": "abc",
"dept": "AB"
}
}
...and...
{
"employee": {
"name": "jhon",
"age": "23",
"salary": "5000"
}
}

Generate nested JSON from stored procedure

I have sample data in a SQL Server table in the following format
CREATE TABLE #tempA
(
HomeId int IDENTITY PRIMARY KEY,
City nvarchar(20),
State nchar(2),
Email VARCHAR(50)
);
INSERT INTO #tempA (City, State, Email)
VALUES ('Cleveland', 'OH', 'sd#aol.com')
INSERT INTO #tempA (City, State, Email)
VALUES ('Malibu', 'CA', 'sd#aol.com')
INSERT INTO #tempA (City, State, Email)
VALUES ('Atlanta', 'GA', 'ploll#aol.com')
SELECT * FROM #tempA
I need a JSON output returned by a stored procedure in the following format, I am trying to group it by email field, I tried using JSON AUTO but not able to achieve in the following format ? Any tips?
[
{
"Email": "sd#aol.com",
"Tasks": [
{
"City": "Cleveland",
"State": "OH"
},
{
"City": "Malibu",
"State": "CA"
}
]
},
{
"Email": "ploll#aol.com",
"Tasks": [
{
"City": "Atlanta",
"State": "GA"
}
]
}
]
One method would be to use a subquery for the State and Email values and group on the email column in the outer query:
SELECT A.Email,
(SELECT B.City,
B.State
FROM #tempA B
WHERE A.Email = B.Email
ORDER BY B.City ASC
FOR JSON AUTO) AS Tasks
FROM #tempA A
GROUP BY A.Email
ORDER BY A.Email DESC
FOR JSON AUTO;
Which gives:
[
{
"Email": "sd#aol.com",
"Tasks": [
{
"City": "Cleveland",
"State": "OH"
},
{
"City": "Malibu",
"State": "CA"
}
]
},
{
"Email": "ploll#aol.com",
"Tasks": [
{
"City": "Atlanta",
"State": "GA"
}
]
}
]

SQL JSON : Nest few nodes under a custom node using sql query FOR JSON

So I have written a query that gives the output as an array of JSON objects, one of the objects is below
{
"cardType": "abc",
"createdOnDateTime": "2020-03-26",
"courseName": "course1",
"courseID": 1,
"sectionName": 1,
"studentList": [
{
"name": "student 1",
"nameLink": "0"
},
{
"name": "student 2",
"nameLink": "0"
},
{
"name": "student 3",
"nameLink": "0"
}
]
}
But I want the output as
{
"cardType": "abc",
"createdOnDateTime": "2020-03-26",
"payload" : {
"courseName": "course1",
"courseID": 1,
"sectionName": 1,
"studentList": [
{
"name": "student 1",
"nameLink": "0"
},
{
"name": "student 2",
"nameLink": "0"
},
{
"name": "student 3",
"nameLink": "0"
}
]
}
}
I have used 'For JSON Auto' phrase at the end of my Select query and as the course and student has a One-to-many relation, the student gets formatted in an array. What I want is that few nodes along with the "studentList" node array should be nested under a custom node 'payload'. How can this be achieved in SQL query using the For JSON and it's related properties?
Totally guessing here on what your data and query looks like based on what little you've given us so far. Use a subquery for studentList with for json auto then, on your outer query use for json path, without_array_wrapper.
When using for json path you can nest elements inside each other by giving them dot-separated paths, i.e.: separating parent elements from children with period (.) characters, such as the following...
create table dbo.Course (
cardType nvarchar(3),
createdOnDateTime date,
courseName nvarchar(20),
courseID int,
sectionName int
);
insert dbo.Course values
('abc', '2020-03-26', 'course1', 1, 1);
go
create table dbo.Student (
courseID int,
name nvarchar(20),
nameLink nvarchar(20)
);
insert dbo.Student values
(1, 'student 1', '0'),
(1, 'student 2', '0'),
(1, 'student 3', '0');
go
select
cardType,
createdOnDateTime,
[payload.courseName] = courseName,
[payload.courseID] = courseID,
[payload.sectionName] = sectionName,
[payload.studentList] = (
select name, nameLink
from dbo.Student S1
where S1.courseID = C1.courseID
for json auto
)
from dbo.Course C1
where courseID = 1
for json path, without_array_wrapper;
go
Which yields the result...
{
"cardType": "abc",
"createdOnDateTime": "2020-03-26",
"payload": {
"courseName": "course1",
"courseID": 1,
"sectionName": 1,
"studentList": [
{
"name": "student 1",
"nameLink": "0"
},
{
"name": "student 2",
"nameLink": "0"
},
{
"name": "student 3",
"nameLink": "0"
}
]
}
}
My query was resolved eventually, I found this really helpful video on youtube that exactly shows what I had to do. Click here for the video.
Also one more thing, this video is a very good example, but for large size data, this approach makes the query very slow.

JSON from SQL query with child level

I have a simple query in my database:
SELECT id, name FROM users FOR JSON AUTO, ROOT('users')
This returns the following JSON:
{
"users": [
{"id": "1", "name": "John"}
{"id": "2", "name": "Mike"}
]
}
I want to have the return with the following format:
{
"users": {
"list": [
{"id": "1", "name": "John"}
{"id": "2", "name": "Mike"}
]
}
}
Can I do this on SQL level by simply changing the query?
You may try with this:
Table:
CREATE TABLE users (
id varchar(1),
[name] varchar(50)
)
INSERT INTO users
(id, [name])
VALUES
('1', 'John'),
('2', 'Mike')
Statement:
SELECT users = (SELECT id, name FROM users FOR JSON AUTO, ROOT('list'))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
Result:
{"users":{"list":[{"id":"1","name":"John"},{"id":"2","name":"Mike"}]}}

SQL Server - How to transform JSON to Relational database

How can we use SQL to Convert a JSON statement into different tables?
For example we have JSON:
{"table1":
{"Name":"table1","Items":
[{"Id":1,"FirstName":"John",
"LastName":"Wen","Country":"UK",
"PostCode":1234,"Status":false,
"Date":"2018-09-18T08:30:32.91",}]},
"table2":
{"Name":"table2","Items":
[{"Id":1,"Name":"leo",
"StudentId":102,"CreatedDate":"2018-09-18","Location":"USA"}]}}
In the relational database, we will get two tables once the JSON is converted
For example, schema 'Table1':
Id FirstName LastName Country PostCode Status Date
1 John Wen UK 1234 false 2018-09-18T08:30:32.91
And the 'Table2' will look like:
Id Name StudentId CreateDate Location
1 Leo 102 2018-9-18 USA
Could anyone please give any advices on it.
You can do this using openjson and json_value functions. Try the following:
Declare #json nvarchar(max),#table1Items nvarchar(max), #table2Items nvarchar(max)
set #json='{
"table1": {
"Name": "table1",
"Items": [{
"Id": 1,
"FirstName": "John",
"LastName": "Wen",
"Country": "UK",
"PostCode": 1234,
"Status": false,
"Date": "2018-09-18T08:30:32.91"
}, {
"Id": 2,
"FirstName": "John1",
"LastName": "Wen1",
"Country": "UK1",
"PostCode": 12341,
"Status": true,
"Date": "2018-09-15T08:30:32.91"
}]
},
"table2": {
"Name": "table2",
"Items": [{
"Id": 1,
"Name": "leo",
"StudentId": 102,
"CreatedDate": "2018-09-18",
"Location": "USA"
}]
}
}'
set #table1Items=(select value from OpenJSON((select value from OpenJSON(#Json) where [key]='table1')) where [key]='Items')
set #table2Items=(select value from OpenJSON((select value from OpenJSON(#Json) where [key]='table2')) where [key]='Items')
--select for table 1
select JSON_VALUE(val,'$.Id') as ID,
JSON_VALUE(val,'$.FirstName') as FirstName,
JSON_VALUE(val,'$.LastName') as LastName,
JSON_VALUE(val,'$.Country') as Country,
JSON_VALUE(val,'$.PostCode') as PostCode,
JSON_VALUE(val,'$.Status') as Status,
JSON_VALUE(val,'$.Date') as Date
from
(
select value as val from openJSON(#table1Items)
) AS Table1JSON
--select for table 1
select JSON_VALUE(val,'$.Id') as ID,
JSON_VALUE(val,'$.Name') as FirstName,
JSON_VALUE(val,'$.StudentId') as LastName,
JSON_VALUE(val,'$.CreatedDate') as Country,
JSON_VALUE(val,'$.Location') as PostCode
from
(
select value as val from openJSON(#table2Items)
) AS Table2JSON
It is working exactly as you wanted. At the end, the two select statements return the tables as you mentioned. Just add them to your desired table using insert into select. I have also tried adding another object to table1 array and verified that it is working fine i.e. returning two rows for two objects. Hope this helps
If SQL Version 2016+ use OPENJSON AND with_clause:
https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-2017
DECLARE #JsonData NVARCHAR(MAX);
SET #JsonData = N'
{
"table1": {
"Name": "table1",
"Items": [
{
"Id": 1,
"FirstName": "John",
"LastName": "Wen",
"Country": "UK",
"PostCode": 1234,
"Status": false,
"Date": "2018-09-18T08:30:32.91"
},
{
"Id": 2,
"FirstName": "John1",
"LastName": "Wen1",
"Country": "UK1",
"PostCode": 12341,
"Status": true,
"Date": "2018-09-15T08:30:32.91"
}
]
},
"table2": {
"Name": "table2",
"Items": [
{
"Id": 1,
"Name": "leo",
"StudentId": 102,
"CreatedDate": "2018-09-18",
"Location": "USA"
}
]
}
}
';
--Table1
SELECT [a].[Id]
, [a].[FistName]
, [a].[Lastname]
, [a].[Country]
, [a].[PostCode]
, [a].[Status]
, [a].[Date]
FROM
OPENJSON(#JsonData, '$.table1.Items')
WITH (
[Id] INT '$.Id'
, [FistName] NVARCHAR(200) '$.FirstName'
, [Lastname] NVARCHAR(200) '$.LastName'
, [Country] NVARCHAR(200) '$.Country'
, [PostCode] NVARCHAR(200) '$.PostCode'
, [Status] NVARCHAR(200) '$.Status'
, [Date] DATETIME '$.Date'
) [a];
--Table2
SELECT [a].[Id]
, [a].[Name]
, [a].[StudentId]
, [a].[CreatedDate]
, [a].[Location]
FROM
OPENJSON(#JsonData, '$.table2.Items')
WITH (
[Id] INT '$.Id'
, [Name] NVARCHAR(200) '$.Name'
, [StudentId] INT '$.StudentId'
, [CreatedDate] DATETIME '$.CreatedDate'
, [Location] NVARCHAR(200) '$.Location'
) [a];
Try using OPENJSON function in SQL Server:
ReferenceLink_1
ReferenceLink_2