Convert select query results into Json - sql

I want to execute a select query on a table and I need this result in a JSON format or in an excel sheet. I want to do this using query only and I'm using SQL Server 2014.
Here is the table schema
CREATE TABLE TestTable
(
Id int primary key identity(1,1),
Name nvarchar(200),
About nvarchar(2000),
Age int,
AddressId int
)
I need to get values from Id, Name, About and Age into a JSON List

JSON AUTO would make quick work of this in but JSON support is available only in SQL Server 2016 and later, including Azure SQL Database. For a T-SQL solution in SQL 2014 and earlier, you'll need to build the JSON string yourself.
Below is an example that uses a FOR XML subquery to concatenate the result into JSON format and adds the outermost [ and ] for the array. Note that this does not handle characters that must be escaped in JSON (\ and ") so you'll need to use REPLACE to escape those if contained in your data.
SELECT '[' + STUFF((
SELECT
',{'
+ '"Id":'+CAST(Id AS varchar(10)) + ','
+ COALESCE('"Name":"' + Name + '",','')
+ COALESCE('"About":"' + About + '",','')
+ COALESCE('"Age":'+CAST(Age AS varchar(10)) + ',','')
+ COALESCE('"AddressId":'+CAST(AddressId AS varchar(10)), '')
+ '}'
FROM TestTable
FOR XML PATH(''), TYPE).value('.', 'varchar(MAX)'),1,1,'')
+ ']';

For SQL server 2017
CREATE TABLE mytable(
ID int PRIMARY KEY,
Name varchar(50),
teamName varchar(255),
Create_Date DATETIME
);
INSERT INTO mytable VALUES (1,NULL,'TEST1','2017-01-02');
INSERT INTO mytable VALUES (2,NULL,'TEST2',NULL);
INSERT INTO mytable VALUES (3,'KK','TEST3','2017-01-02');
INSERT INTO mytable VALUES (4,NULL,NULL,NULL);
Try below way here i provide an example
SELECT
ID,
Name,
teamName,
Create_Date
FROM mytable
FOR JSON AUTO
http://www.sqlfiddle.com/#!18/81350/1
JSON_F52E2B61-18A1-11d1-B105-00805F49916B
[{"ID":1,"teamName":"TEST1","Create_Date":"2017-01-02T00:00:00"},{"ID":2,"teamName":"TEST2"},{"ID":3,"Name":"KK","teamName":"TEST3","Create_Date":"2017-01-02T00:00:00"},{"ID":4}]
For below vesion of sql 2017 server:
1st create a scaler function
create FUNCTION [dbo].[udf-Str-JSON] (#IncludeHead int,#ToLowerCase int,#XML xml)
Returns varchar(max)
AS
Begin
Declare #Head varchar(max) = '',#JSON varchar(max) = ''
; with cteEAV as (Select RowNr=Row_Number() over (Order By (Select NULL))
,Entity = xRow.value('#*[1]','varchar(100)')
,Attribute = xAtt.value('local-name(.)','varchar(100)')
,Value = xAtt.value('.','varchar(max)')
From #XML.nodes('/row') As R(xRow)
Cross Apply R.xRow.nodes('./#*') As A(xAtt) )
,cteSum as (Select Records=count(Distinct Entity)
,Head = IIF(#IncludeHead=0,IIF(count(Distinct Entity)<=1,'[getResults]','[[getResults]]'),Concat('{"status":{"successful":"true","timestamp":"',Format(GetUTCDate(),'yyyy-MM-dd hh:mm:ss '),'GMT','","rows":"',count(Distinct Entity),'"},"results":[[getResults]]}') )
From cteEAV)
,cteBld as (Select *
,NewRow=IIF(Lag(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,'',',{')
,EndRow=IIF(Lead(Entity,1) over (Partition By Entity Order By (Select NULL))=Entity,',','}')
,JSON=Concat('"',IIF(#ToLowerCase=1,Lower(Attribute),Attribute),'":','"',Value,'"')
From cteEAV )
Select #JSON = #JSON+NewRow+JSON+EndRow,#Head = Head From cteBld, cteSum
Return Replace(#Head,'[getResults]',Stuff(#JSON,1,1,''))
End
-- Parameter 1: #IncludeHead 1/0
-- Parameter 2: #ToLowerCase 1/0 (converts field name to lowercase
-- Parameter 3: (Select * From ... for XML RAW)
Then use this function json conversion below query is an example
Declare #Table table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50))
Insert into #Table values
(1,1,'John','Smith','john.smith#email.com'),
(2,0,'Jane','Doe' ,'jane.doe#email.com')
Select A.ID
,A.Last_Name
,A.First_Name
,B.JSON
From #Table A
Cross Apply (Select JSON=[dbo].[udf-Str-JSON](0,1,(Select A.* For XML Raw)) ) B

From SQL-Server 2016+, you can use JSON AUTO to return Json:
Select *
From Customers
FOR JSON AUTO;
If you expect to return just a single row, then you can add Without_Array_Wrapper after FOR JSON AUTO with a comma after the AUTO keyword, and you will get an object instead of an array:
Select *
From Customers
FOR JSON AUTO, Without_Array_Wrapper;
Note: If you add the Without_Array_Wrapper and return multiple rows, you will get a newly populated object type but won't get any error - which requires carefulness as you won't know if anything is wrong and might hit you later when exercising the data.

From SQL-Server 2016+, you can use JSON AUTO to return Json:
I know two way to return query in JSON format.
If your query is from one table or only return a single table (as usual) you can use JSON PATH or JSON AUTO
CREATE TABLE TestTable
(
Id int primary key identity(1,1),
Name nvarchar(200),
About nvarchar(2000),
Age int,
AddressId int
)
INSERT INTO dbo.TestTable(Name,About,Age,AddressId)
VALUES (N'John' , NULL ,21, 16),(N'Mehdi' , 'Developer' ,32, 15)
query is
select * from [TestTable] for JSON PATH
output :
[{
"Id": 1,
"Name": "Mehdi",
"About": "Developer",
"Age": 32,
"AddressId": 15
}, {
"Id": 3,
"Name": "John",
"Age": 21,
"AddressId": 16
}
]
Notice that NULL values do not show on result. For showing the null values, [INCLUDE_NULL_VALUES] must be added at the end of the query.
SELECT * FROM [TestTable] WHERE id=2 FOR JSON PATH , INCLUDE_NULL_VALUES
output:
[{
"Id": 3,
"Name": "John",
"About": null,
"Age": 21,
"AddressId": 16
}
]
For the query that only return an object, WITHOUT_ARRAY_WRAPPER must be added at the end of query the brackets are remove from the result.
SELECT * FROM [TestTable] WHERE id=2 FOR JSON PATH , INCLUDE_NULL_VALUES , WITHOUT_ARRAY_WRAPPER
output :
{
"Id": 3,
"Name": "John",
"About": null,
"Age": 21,
"AddressId": 16
}
For adding sub query for example the user's contracts or user's family names.
there two way :
1-
SELECT
* ,
ContractList = (SELECT* FROM contract where UserId = 2 FOR JSON PATH)
FROM TestTable FOR JSON PATH
2-
SELECT
* ,
(JSON_QUERY((SELECT* FROM contract WHERE UserId = 2 FOR JSON PATH))) AS ContractList
FROM TestTable FOR JSON PATH
output:
[{
"Id": 3,
"Name": "John",
"About": null,
"Age": 21,
"AddressId": 16 ,
"ContractList" : [{...}, {...}]
}
]

Thank you so much, your answer came to my rescue after a week-long search.
Had to make a few changes to suit my scenario and add quotes around the code
SELECT '[' + STUFF((
SELECT
',['
+ '"'+ CAST(s.Code AS varchar(10)) + '"' + ','
+ COALESCE(''+CAST(COUNT(s.ControlNumber) AS varchar(10)), '')
+ ']'
FROM school s
INNER JOIN [dbo].[Class] cc ON cc.SchoolID = s.SchoolID
INNER JOIN [dbo].[Subject] s ON s.subjectID = cc.subjectID
INNER JOIN [dbo].Marks ms ON s.MarkID = c.CaseStatusID
WHERE ms.Created BETWEEN '2021-04-01 00:00:00' AND '2021-04-07 23:59:59'
AND s.subjectID IN (1, 2, 3, 4, 5, 6, 7, 8, 9)
Group By s.Code
FOR XML PATH(''), TYPE).value('.', 'varchar(MAX)'),1,1,'')
+ ']';
Output
[["za-ec",14],["za-fs",5],["za-gt",3],["za-mp",14],["za-nc",2],["za-nl",8],["za-np",5],["za-nw",6],["za-wc",15]]

Related

SQL Select with JSON Column and Group By [duplicate]

SQL 2016 has a new feature which converts data on SQL server to JSON. I am having difficulty in combining array of objects into array of values i.e.,
EXAMPLE -
CREATE TABLE #temp (item_id VARCHAR(256))
INSERT INTO #temp VALUES ('1234'),('5678'),('7890')
SELECT * FROM #temp
--convert to JSON
SELECT (SELECT item_id
FROM #temp
FOR JSON PATH,root('ids'))
RESULT -
{
"ids": [{
"item_id": "1234"
},
{
"item_id": "5678"
},
{
"item_id": "7890"
}]
}
But I want the result as -
"ids": [
"1234",
"5678",
"7890"
]
Can somebody please help me out?
Thanks! The soultion we found is converting into XML first -
SELECT
JSON_QUERY('[' + STUFF(( SELECT ',' + '"' + item_id + '"'
FROM #temp FOR XML PATH('')),1,1,'') + ']' ) ids
FOR JSON PATH , WITHOUT_ARRAY_WRAPPER
Martin!
I believe this is an even simpler way of doing it:
SELECT '"ids": ' +
REPLACE(
REPLACE( (SELECT item_id FROM #temp FOR JSON AUTO),'{"item_id":','' ),
'"}','"' )
declare #temp table (item_id VARCHAR(256))
INSERT INTO #temp VALUES ('1234'),('5678'),('7890')
SELECT * FROM #temp
--convert to JSON
select
json_query(QUOTENAME(STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', char(44)))) as [json]
from #temp
for json path
When we want to concatenate strings as json array then:
escape string - STRING_ESCAPE
concatenate string with comma separator - STRING_AGG, comma ascii code is 44
add quotation it in brackets - QUOTENAME (without param)
return string (with array of elements) as json - JSON_QUERY
Since arrays of primitive values are valid JSON, it seems strange that a facility for selecting arrays of primitive values isn't built into SQL Server's JSON functionality. (If on the contrary such functionality exists, I at least haven't been able to discover it after quite a bit of searching).
The approach outlined above works as described. But when applied for a field in a larger query, the array of primitives is surrounded with quotes.
E.g., this
DECLARE #BomTable TABLE (ChildNumber dbo.udt_ConMetPartNumber);
INSERT INTO #BomTable (ChildNumber) VALUES (N'101026'), (N'101027');
SELECT N'"Children": ' + REPLACE(REPLACE((SELECT ChildNumber FROM #BomTable FOR JSON PATH), N'{"ChildNumber":', N''), '"}','');
works by producing:
"Children": ["101026,"101027]
But, following the approach above, this:
SELECT
p.PartNumber,
p.Description,
REPLACE(REPLACE((SELECT
ChildNumber
FROM
Part.BillOfMaterials
WHERE
ParentNumber = p.PartNumber
ORDER BY
ChildNumber
FOR
JSON AUTO
), N'{"ChildNumber":', N''), '"}', '"') AS [Children]
FROM
Part.Parts AS p
WHERE
p.PartNumber = N'104444'
FOR
JSON PATH
Produces:
[
{
"PartNumber": "104444",
"Description": "ASSY HUB R-SER DRIV HP10 ABS",
"Children": "[\"101026\",\"101027\",\"102291\",\"103430\",\"103705\",\"104103\"]"
}
]
Where the Children array is wrapped as a string.
This version (building on the others):
correctly escapes an special JSON characters (e.g. quotes)
returns an empty array [] for no data
Requires SQL 2017 or later (due to STRING_AGG):
SELECT
CONCAT('[',
(SELECT STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', ',')
FROM #temp)
, ']')
Here's a wild idea that may or may not be practical. Recurse over your data set and append things to your JSON arrays using JSON_MODIFY:
with
d (d) as (select * from (values (1),(2),(3),(4)) t (d)),
j (d, j) as (
-- Adapt the recursion to make it dynamic
select 1, json_modify('[]', 'append $', d)
from d
where d = 1
union all
select d.d, json_modify(j, 'append $', d.d)
from d join j on d.d = j.d + 1
)
select *
from j;
I kept it simple for illustration purposes. You'll adapt it to make it dynamic, of course. This produces:
|d |j |
|---|---------|
|1 |[1] |
|2 |[1,2] |
|3 |[1,2,3] |
|4 |[1,2,3,4]|
Could even be used to emulate standard SQL JSON_ARRAYAGG
Most of these solutions are essentially creating a CSV that represents the array contents, and then putting that CSV into the final JSON format. Here's what I use, to avoid XML:
DECLARE #tmp NVARCHAR(MAX) = ''
SELECT #tmp = #tmp + '"' + [item_id] + '",'
FROM #temp -- Defined and populated in the original question
SELECT [ids] = JSON_QUERY((
SELECT CASE
WHEN #tmp IS NULL THEN '[]'
ELSE '[' + SUBSTRING(#tmp, 0, LEN(#tmp)) + ']'
END
))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER

How to parse JSON string recursively with openjson

I have the following JSON data :
set #json = N'{
"Book":{
"IssueDate":"02-15-2019"
, "Detail":{
"Type":"Any Type"
, "Author":{
"Name":"Annie"
, "Sex":"Female"
}
}
, "Chapter":[
{
"Section":"1.1"
, "Title":"Hello world."
}
,
{
"Section":"1.2"
, "Title":"Be happy."
}
]
, "Sponsor":["A","B","C"]
}
}'
The expected result is
topKey Key Value
Book IssueDate 02-15-2019
Book Detail { "Type":"Any Type", "Author":{ "Name":"Annie" , "Sex":"Female"}
Book Chapter [{ "Section":"1.1", "Title":"Hello world." }, { "Section":"1.2", "Title":"Be happy." }]
Book Sponsor ["A","B","C"]
Detail Type Any Type
Detail Author { "Name":"Annie" ,"Sex":"Female"}
Author Name Annie
Author Sex Female
Chapter Section 1.1
Chapter Title Hello world
Chapter Section 1.2
Chapter Title Be happy.
I found that when the field "Value" is JSON, I need to keep parsing it.
So I created a function to do the parsing work but it returns '' which does not meet the requirement.
create function ParseJson(#json nvarchar(max))
returns #tempTable table ([key] nvarchar(max), [value] nvarchar(max))
as
begin
insert #tempTable
select
x.[key]
, x.[value]
from
openjson(#json) x
cross apply ParseJson(x.[value]) y
where ISJSON(x.[value])=1
end
A string may be passed to the function.
select * from ParseJson(#json)
I'm not sure if your expectation of the results is reasonable but clearly the returning table of your function doesn't match what you stated -- it lacks topKey column. For this reason, I'd rather aggregate the path of the hierarchy. Here we go:
create function ParseJson(
#parent nvarchar(max), #json nvarchar(max))
returns #tempTable table (
[key] nvarchar(max), [value] nvarchar(max))
as
begin
; with cte as (
select
iif(#parent is null, [key]
, concat(#parent, '.', [key])) [key]
, [value]
from
openjson(#json)
)
insert
#tempTable
select
x.*
from
cte x
union all
select
x.*
from
cte y
cross apply ParseJson(y.[key], y.[value]) x
where isjson(y.[value])=1
return
end
And the results:

SQL Server - Creating an inner JSON query that returns flattened values [duplicate]

SQL 2016 has a new feature which converts data on SQL server to JSON. I am having difficulty in combining array of objects into array of values i.e.,
EXAMPLE -
CREATE TABLE #temp (item_id VARCHAR(256))
INSERT INTO #temp VALUES ('1234'),('5678'),('7890')
SELECT * FROM #temp
--convert to JSON
SELECT (SELECT item_id
FROM #temp
FOR JSON PATH,root('ids'))
RESULT -
{
"ids": [{
"item_id": "1234"
},
{
"item_id": "5678"
},
{
"item_id": "7890"
}]
}
But I want the result as -
"ids": [
"1234",
"5678",
"7890"
]
Can somebody please help me out?
Thanks! The soultion we found is converting into XML first -
SELECT
JSON_QUERY('[' + STUFF(( SELECT ',' + '"' + item_id + '"'
FROM #temp FOR XML PATH('')),1,1,'') + ']' ) ids
FOR JSON PATH , WITHOUT_ARRAY_WRAPPER
Martin!
I believe this is an even simpler way of doing it:
SELECT '"ids": ' +
REPLACE(
REPLACE( (SELECT item_id FROM #temp FOR JSON AUTO),'{"item_id":','' ),
'"}','"' )
declare #temp table (item_id VARCHAR(256))
INSERT INTO #temp VALUES ('1234'),('5678'),('7890')
SELECT * FROM #temp
--convert to JSON
select
json_query(QUOTENAME(STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', char(44)))) as [json]
from #temp
for json path
When we want to concatenate strings as json array then:
escape string - STRING_ESCAPE
concatenate string with comma separator - STRING_AGG, comma ascii code is 44
add quotation it in brackets - QUOTENAME (without param)
return string (with array of elements) as json - JSON_QUERY
Since arrays of primitive values are valid JSON, it seems strange that a facility for selecting arrays of primitive values isn't built into SQL Server's JSON functionality. (If on the contrary such functionality exists, I at least haven't been able to discover it after quite a bit of searching).
The approach outlined above works as described. But when applied for a field in a larger query, the array of primitives is surrounded with quotes.
E.g., this
DECLARE #BomTable TABLE (ChildNumber dbo.udt_ConMetPartNumber);
INSERT INTO #BomTable (ChildNumber) VALUES (N'101026'), (N'101027');
SELECT N'"Children": ' + REPLACE(REPLACE((SELECT ChildNumber FROM #BomTable FOR JSON PATH), N'{"ChildNumber":', N''), '"}','');
works by producing:
"Children": ["101026,"101027]
But, following the approach above, this:
SELECT
p.PartNumber,
p.Description,
REPLACE(REPLACE((SELECT
ChildNumber
FROM
Part.BillOfMaterials
WHERE
ParentNumber = p.PartNumber
ORDER BY
ChildNumber
FOR
JSON AUTO
), N'{"ChildNumber":', N''), '"}', '"') AS [Children]
FROM
Part.Parts AS p
WHERE
p.PartNumber = N'104444'
FOR
JSON PATH
Produces:
[
{
"PartNumber": "104444",
"Description": "ASSY HUB R-SER DRIV HP10 ABS",
"Children": "[\"101026\",\"101027\",\"102291\",\"103430\",\"103705\",\"104103\"]"
}
]
Where the Children array is wrapped as a string.
This version (building on the others):
correctly escapes an special JSON characters (e.g. quotes)
returns an empty array [] for no data
Requires SQL 2017 or later (due to STRING_AGG):
SELECT
CONCAT('[',
(SELECT STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', ',')
FROM #temp)
, ']')
Here's a wild idea that may or may not be practical. Recurse over your data set and append things to your JSON arrays using JSON_MODIFY:
with
d (d) as (select * from (values (1),(2),(3),(4)) t (d)),
j (d, j) as (
-- Adapt the recursion to make it dynamic
select 1, json_modify('[]', 'append $', d)
from d
where d = 1
union all
select d.d, json_modify(j, 'append $', d.d)
from d join j on d.d = j.d + 1
)
select *
from j;
I kept it simple for illustration purposes. You'll adapt it to make it dynamic, of course. This produces:
|d |j |
|---|---------|
|1 |[1] |
|2 |[1,2] |
|3 |[1,2,3] |
|4 |[1,2,3,4]|
Could even be used to emulate standard SQL JSON_ARRAYAGG
Most of these solutions are essentially creating a CSV that represents the array contents, and then putting that CSV into the final JSON format. Here's what I use, to avoid XML:
DECLARE #tmp NVARCHAR(MAX) = ''
SELECT #tmp = #tmp + '"' + [item_id] + '",'
FROM #temp -- Defined and populated in the original question
SELECT [ids] = JSON_QUERY((
SELECT CASE
WHEN #tmp IS NULL THEN '[]'
ELSE '[' + SUBSTRING(#tmp, 0, LEN(#tmp)) + ']'
END
))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
I think the following would be easier in SQL server 2017
select
JSON_QUERY
(
'["' + STRING_AGG(t.item_id,'","') + '"]'
) as ids
from #temp t
for json auto, without_array_wrapper

SQL to JSON - How to get query output without column names then put together the two columns

For example, a table called User has columns FirstName and LastName. So when I query
SELECT * FROM User FOR JSON PATH
The output is
[{ "FirstName": "Bruce",
"LastName": "Wayne"
}]
But the output I am trying to get is
[{ "Bruce" : "Wayne" }]
In JSON, the structure is "Property" : "Value". You are attempting to create a JSON with the structure "Value" : "Value".
I'm not sure why you would want such a thing or even if it's a good idea, but to get to this you can create the output string yourself as gotqn suggested in his comment instead of using FOR JSON.
First, create and populate sample table (Please save us this step in your future questions):
CREATE TABLE [user]
(
FirstName varchar(10),
LastName varchar(10)
)
INSERT INTO [User] (FirstName, LastName) VALUES
('Bruce', 'Wayne'),
('Clark', 'Kent'),
('Peter', 'Parker'),
('Tony', 'Stark')
The query:
SELECT STUFF(
(
SELECT ',{"'+ FirstName +'" : "'+ LastName +'"}'
FROM [User]
FOR XML PATH('')
)
, 1, 1, '[') + ']' As JsonOutput
Results:
JsonOutput
[{"Bruce" : "Wayne"},{"Clark" : "Kent"},{"Peter" : "Parker"},{"Tony" : "Stark"}]
SELECT STUFF(
(
SELECT ',"'+ cast( ColumnName AS nvarchar) +'" : "'+ cast(ColumnName AS nvarchar) +'"'
FROM Site_Culture
FOR XML PATH('')
)
, 1, 1,'') As JsonOutput

SQL to JSON - array of objects to array of values in SQL 2016

SQL 2016 has a new feature which converts data on SQL server to JSON. I am having difficulty in combining array of objects into array of values i.e.,
EXAMPLE -
CREATE TABLE #temp (item_id VARCHAR(256))
INSERT INTO #temp VALUES ('1234'),('5678'),('7890')
SELECT * FROM #temp
--convert to JSON
SELECT (SELECT item_id
FROM #temp
FOR JSON PATH,root('ids'))
RESULT -
{
"ids": [{
"item_id": "1234"
},
{
"item_id": "5678"
},
{
"item_id": "7890"
}]
}
But I want the result as -
"ids": [
"1234",
"5678",
"7890"
]
Can somebody please help me out?
Thanks! The soultion we found is converting into XML first -
SELECT
JSON_QUERY('[' + STUFF(( SELECT ',' + '"' + item_id + '"'
FROM #temp FOR XML PATH('')),1,1,'') + ']' ) ids
FOR JSON PATH , WITHOUT_ARRAY_WRAPPER
Martin!
I believe this is an even simpler way of doing it:
SELECT '"ids": ' +
REPLACE(
REPLACE( (SELECT item_id FROM #temp FOR JSON AUTO),'{"item_id":','' ),
'"}','"' )
declare #temp table (item_id VARCHAR(256))
INSERT INTO #temp VALUES ('1234'),('5678'),('7890')
SELECT * FROM #temp
--convert to JSON
select
json_query(QUOTENAME(STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', char(44)))) as [json]
from #temp
for json path
When we want to concatenate strings as json array then:
escape string - STRING_ESCAPE
concatenate string with comma separator - STRING_AGG, comma ascii code is 44
add quotation it in brackets - QUOTENAME (without param)
return string (with array of elements) as json - JSON_QUERY
Since arrays of primitive values are valid JSON, it seems strange that a facility for selecting arrays of primitive values isn't built into SQL Server's JSON functionality. (If on the contrary such functionality exists, I at least haven't been able to discover it after quite a bit of searching).
The approach outlined above works as described. But when applied for a field in a larger query, the array of primitives is surrounded with quotes.
E.g., this
DECLARE #BomTable TABLE (ChildNumber dbo.udt_ConMetPartNumber);
INSERT INTO #BomTable (ChildNumber) VALUES (N'101026'), (N'101027');
SELECT N'"Children": ' + REPLACE(REPLACE((SELECT ChildNumber FROM #BomTable FOR JSON PATH), N'{"ChildNumber":', N''), '"}','');
works by producing:
"Children": ["101026,"101027]
But, following the approach above, this:
SELECT
p.PartNumber,
p.Description,
REPLACE(REPLACE((SELECT
ChildNumber
FROM
Part.BillOfMaterials
WHERE
ParentNumber = p.PartNumber
ORDER BY
ChildNumber
FOR
JSON AUTO
), N'{"ChildNumber":', N''), '"}', '"') AS [Children]
FROM
Part.Parts AS p
WHERE
p.PartNumber = N'104444'
FOR
JSON PATH
Produces:
[
{
"PartNumber": "104444",
"Description": "ASSY HUB R-SER DRIV HP10 ABS",
"Children": "[\"101026\",\"101027\",\"102291\",\"103430\",\"103705\",\"104103\"]"
}
]
Where the Children array is wrapped as a string.
This version (building on the others):
correctly escapes an special JSON characters (e.g. quotes)
returns an empty array [] for no data
Requires SQL 2017 or later (due to STRING_AGG):
SELECT
CONCAT('[',
(SELECT STRING_AGG('"' + STRING_ESCAPE(item_id, 'json') + '"', ',')
FROM #temp)
, ']')
Here's a wild idea that may or may not be practical. Recurse over your data set and append things to your JSON arrays using JSON_MODIFY:
with
d (d) as (select * from (values (1),(2),(3),(4)) t (d)),
j (d, j) as (
-- Adapt the recursion to make it dynamic
select 1, json_modify('[]', 'append $', d)
from d
where d = 1
union all
select d.d, json_modify(j, 'append $', d.d)
from d join j on d.d = j.d + 1
)
select *
from j;
I kept it simple for illustration purposes. You'll adapt it to make it dynamic, of course. This produces:
|d |j |
|---|---------|
|1 |[1] |
|2 |[1,2] |
|3 |[1,2,3] |
|4 |[1,2,3,4]|
Could even be used to emulate standard SQL JSON_ARRAYAGG
Most of these solutions are essentially creating a CSV that represents the array contents, and then putting that CSV into the final JSON format. Here's what I use, to avoid XML:
DECLARE #tmp NVARCHAR(MAX) = ''
SELECT #tmp = #tmp + '"' + [item_id] + '",'
FROM #temp -- Defined and populated in the original question
SELECT [ids] = JSON_QUERY((
SELECT CASE
WHEN #tmp IS NULL THEN '[]'
ELSE '[' + SUBSTRING(#tmp, 0, LEN(#tmp)) + ']'
END
))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
I think the following would be easier in SQL server 2017
select
JSON_QUERY
(
'["' + STRING_AGG(t.item_id,'","') + '"]'
) as ids
from #temp t
for json auto, without_array_wrapper