SQL to JSON - Create a Json Array - json

I have the below data:
CREATE TABLE mytable
(
ID int,
Product nvarchar(50),
Usage nvarchar(255),
);
INSERT INTO mytable VALUES (99346,'Universal light','Art and Culture');
INSERT INTO mytable VALUES (99346,'Universal light','Health and Care');
INSERT INTO mytable VALUES (99346,'Universal light','Hotel and Wellness');
INSERT INTO mytable VALUES (99346,'Universal light','Education and Science');
And I have created the following code to get my JSON output:
SELECT DISTINCT T1.ID
,T1.Product
,(SELECT T2.Usage
FROM mytable T2
WHERE T1.ID=T2.ID
FOR JSON PATH) as 'Usage'
FROM mytable T1
FOR JSON PATH
It outputs the following results:
[
{
"ID": 99346,
"Product": "Universal light",
"Usage": [
{
"Usage": "Art and Culture"
},
{
"Usage": "Health and Care"
},
{
"Usage": "Hotel and Wellness"
},
{
"Usage": "Education and Science"
}
]
}
]
I would like to have the results as below, but can't figure out how to change the syntax:
[
{
"ID": 99346,
"Product": "Universal light",
"Usage": [ "Art and Culture" , "Health and Care" , "Hotel and Wellness" , "Education and Science"
]
}
]
Any help on this much appreciated.
EDIT
If I use this initial data, where at the end of line 3 I have an extra ' ' the solution does not work, no error or warning:
INSERT INTO mytable VALUES (99346,'Universal light','Art and Culture');
INSERT INTO mytable VALUES (99346,'Universal light','Education and Science');
INSERT INTO mytable VALUES (99346,'Universal light','Health and Care ');
INSERT INTO mytable VALUES (99346,'Universal light','Hotel and Wellness');
INSERT INTO mytable VALUES (99346,'Universal light','Offices and Communication');
INSERT INTO mytable VALUES (99346,'Universal light','Presentation and Retail');
I have tried to use TRIM, as you can see below:
SELECT Distinct ID
,Product
,json_query(QUOTENAME(STRING_AGG('"' + STRING_ESCAPE(TRIM(Usage), 'json') + '"', char(44)))) AS 'Usage'
FROM mytable
GROUP BY ID
,Product
FOR JSON PATH;
Unfortunately it does not work and the whole array 'Usage' is somehow ignored, see results:
[
{
"ID": 99346,
"Product": "Universal light"
}
]

You can use STRING_AGG to build the array like this:
SELECT DISTINCT ID
,Product
,json_query(QUOTENAME(STRING_AGG('"' + STRING_ESCAPE(Usage, 'json') + '"', char(44)))) AS 'Usage'
FROM mytable T1
GROUP BY ID
,Product
FOR JSON PATH;
If you are not using SQL Sever 2017 or later, you can use concatenate the values using XML PATH.
SELECT DISTINCT T1.ID
,T1.Product
,
(
'[' +
STUFF
(
(
SELECT ',' + '"' + T2.Usage + '"'
FROM mytable T2
WHERE T1.ID=T2.ID
FOR XML PATH, TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
)
+ ']'
) as 'Usage'
FROM mytable T1
FOR JSON PATH
For your edit use:
SELECT Distinct ID
,Product
,json_query('[' + (STRING_AGG('"' + STRING_ESCAPE(TRIM(Usage), 'json') + '"', char(44))) + ']') AS 'Usage'
FROM mytable
GROUP BY ID
,Product
FOR JSON PATH;
The issue is QUOTENAME input is limited to 128 chars and returns NULL when you add more records.

Related

TSQL FOR JSON nested value

I am trying to output JSON via tsql hierarchy table.
Code:
select Field1, Field2 from #header inner join #line on #header.Id = #Line.Id FOR JSON AUTO
I am getting:
"Field1": "BOB",
"Field2": "BOB2",
but I am looking for it to display
"Field1": {
"value": "BOB"
},
"Field2": {
"value": "BOB2"
},
What am I doing wrong? I can using text manipulation, but was wondering if there is a "blessed" way, i.e. built-in that is readable and best-practice.
Select STRING_AGG(concat('"',[Key],'":{"value":"',string_escape(Value,'json'),'"}'),',')
From OpenJson( (Select * From #YourTable For JSON Path,Without_Array_Wrapper ) )
For 2016 -- STUFF XML
Select stuff((Select concat('"',[Key],'":{"value":"',string_escape(Value,'json'),'"}')
From OpenJson( (Select * From #YourTable For JSON Path,Without_Array_Wrapper ) )
For XML Path ('')),1,0,'')
Results
"Field1":{
"value":"Bob"
},
"Field2":{
"value":"Bob2"
}
You can just use a path for each column name, combined with FOR JSON PATH.
SELECT
Field1 AS [Field1.value],
Field2 AS [Field2.value]
FROM #header h
JOIN #line l ON h.Id = l.Id
FOR JSON PATH;
If you need the two fields as entirely separate rows each with its own object, you can unpivot it
SELECT
v.[Field1.value],
v.[Field2.value]
FROM #header h
JOIN #line l ON h.Id = l.Id
CROSS APPLY (VALUES
(Field1, NULL),
(NULL, Field2)
) v([Field1.value], [Field2.value])
FOR JSON PATH;

Using JSON_VALUE + JSON_QUERY to create new JSON

I was wondering if it would be possible to reshape JSON and return it as JSON. I have JSON which, in simplified form, looks like:
Name
Details
fieldId
fieldValue
Other
Id
Value
And would like to return:
Name
Details
fieldId
fieldValue
I can return Name and Details with JSON_VALUE and JSON_QUERY but would like it as one combined JSON field.
create table #test (
[id] int,
[json] varchar(max)
);
insert into #test (id, json) values (1, '{
"Name": "Test 1",
"Details": [
{
"fieldId": "100",
"fieldValue": "ABC"
}],
"Other": [
{
"Id": "1",
"Value": "ABC"
}]
}');
insert into #test (id, json) values (2, '{
"Name": "Test 2",
"Details": [
{
"fieldId": "101",
"fieldValue": "ABCD"
}],
"Other": [
{
"Id": "2",
"Value": "ABCD"
}]
}');
select id, JSON_VALUE(json, '$.Name'), JSON_QUERY(json, '$.Details')
from #test
As an additional option, you may parse the JSON with OPENJSON() and explicit schema (columns definitions) and then build the new JSON again:
SELECT
id,
newjson = (
SELECT Name, Details
FROM OPENJSON(json) WITH (
Name varchar(max) '$.Name',
Details nvarchar(max) '$.Details' AS JSON
)
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)
FROM #test
And the same approach with JSON_VALUE() and JSON_QUERY():
SELECT
id,
newjson = (
SELECT JSON_VALUE(json, '$.Name') AS [Name], JSON_QUERY(json, '$.Details') AS [Details]
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)
FROM #test
One solution is to use JSON_MODIFY to re-construct the JSON:
SELECT
id,
JSON_MODIFY(
JSON_MODIFY(
'{}',
'$.Name',
JSON_VALUE(json, '$.Name')
),
'$.Details',
JSON_QUERY(json, '$.Details')
) AS new_json
FROM #test
An alternate would be to delete the Other node using JSON_MODIFY but you have to know the name of node(s) to remove.

Json conversion in SQL Server - multiple rows in to single json array

Dataset :
create table grievances(grivanceid int ,grivancedesc varchar(10))
create table grievanceType(grivanceid int ,grivanceType varchar(10))
insert into grievances values (1,'abc')
insert into grievanceType values (1,'type1')
insert into grievanceType values (1,'type2')
Desired output:
{
"grivanceid": 1,
"grivancedesc": "abc",
"grivanceType": [ "type1", "type2"]
}
My query : not fully achieved
select *
from
(select
a.*,
stuff(list.grivanceType, 1, 1, '') grivanceType
from
grievances a
cross apply
(select
',' + grivanceType
from
grievanceType b
where
grivanceid = a.grivanceid
for xml path ('')
) list(grivanceType)) a
for json path, without_array_wrapper
It helps if you wrap your XML results in a JSON_Query()
Example
Select *
,grivanceType = JSON_QUERY('['+stuff((Select concat(',"',grivanceType,'"' )
From grievanceType
Where grivanceid =A.grivanceid
For XML Path ('')),1,1,'')+']'
)
From grievances A
for json path, without_array_wrapper
Returns
{
"grivanceid": 1,
"grivancedesc": "abc",
"grivanceType": ["type1", "type2"]
}

MySQL JSON - How to use JSON_SET on all elements of a matched path?

I'm trying to figure out a JSON_SET() query that would add {key2: 2} to all elements of $.a:
{
"a": [
{
"key1": 1
},
{
"key1": 1
}
]
}
Obviously, this fails because of '*' in the path:
UPDATE table SET json=JSON_SET(json, '$a[*].key2', 2);
How can this be done?
Thanks!
One option is JSON_TABLE and JSON_ARRAYAGG:
UPDATE `table`
INNER JOIN (
SELECT
JSON_ARRAYAGG(
JSON_SET(`der`.`_json`, '$.key2', 2)
) `json_key2`
FROM
`table`,
JSON_TABLE(`table`.`json`,
'$.a[*]'
COLUMNS(
`_json` JSON PATH '$'
)
) `der`
GROUP BY
`der`.`_json`
) `der`
SET `table`.`json` = JSON_SET(
`table`.`json`,
'$.a',
`der`.`json_key2`
);
See db-fiddle.

Consume JSON via OPENJSON and save in table - SQL Server 2016

We recently upgraded our backend DB from SQL Server 2012 to SQL Server 2016 which I realized supports JSON objects. One of our web services return data in the below format, and I am trying to consume it directly using OPENJSON function.
{
"RESULT_1": {
"columns": ["col1", "col2", "col3", "col4"],
"data": [
["0", null, "12345", "other"],
["1", "a", "54321", "MA"],
["0", null, "76543", "RI"]
]
}
}
I want to make sure that I read the column names from the "columns section" and make sure that the correct data is read and pushed in SQL Server 2016.
DECLARE #Json_Array2 nvarchar(max) = '{
"RESULT_1": {
"columns": ["col1", "col2", "col3", "col4"],
"data": [
["0", null, "12345", "other"],
["1", "a", "54321", "MA"],
["0", null, "76543", "RI"]
]
} }';
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.columns')
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.data')
With above statements, I am able to extract the columns and the data array. Would it be possible to dump this data in a flat table (already existing) with the same column names?
I am able to see all the values of one particular row by:
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.data[0]')
key value
0 0
1 NULL
2 12345
3 other
Any pointers are appreciated.
Thank you
If I understand correctly you are trying to extract all elements from the "data" array
Col1 Col2 Col3 Col4
0 NULL 12345 other
1 a 54321 MA
0 NULL 76543 RI
Suppose your Json Structure will not change you can achieve that using the following query:
SET NOCOUNT ON
IF OBJECT_ID ('tempdb..##ToPvt') IS NOT NULL DROP TABLE ##ToPvt
DECLARE #Cmd NVARCHAR(MAX)=''
DECLARE #Table TABLE (Col1 nvarchar(100), Col2 nvarchar(100), Col3 nvarchar(100) , Col4 nvarchar(100))
DECLARE #Json_Array2 nvarchar(max) = '{
"RESULT_1": {
"columns": ["col1", "col2", "col3", "col4"],
"data": [
["0", null, "12345", "other"],
["1", "a", "54321", "MA"],
["0", null, "76543", "RI"]
]
} }';
;WITH Cols ([Key], [Value]) as
(
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.columns')
)
, [Rows] as
(
SELECT ROW_NUMBER () OVER (ORDER BY [Key]) Seq, [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.data')
)
,ToPvt as
(
SELECT c.[Key]+1 Cols, x.Value , 'col'+CONVERT(VARCHAR(10),ROW_NUMBER () OVER (PARTITION BY c.Value ORDER BY t.[Key])) Seq
FROM [Rows] t
INNER JOIN Cols C
ON t.[key] = c.[key]
CROSS APPLY OPENJSON(t.value) X
)
SELECT *
INTO ##ToPvt
FROM ToPvt
;WITH Final (Cmd) as
(
SELECT DISTINCT 'SELECT [col1], [col2], [col3],[col4] FROM ##ToPvt
PIVOT
(
MAX([Value]) FOR Seq IN ([col1], [col2], [col3],[col4])
) T
WHERE Cols = '+CONVERT(VARCHAR(10),Cols)+'
;'
FROM ##ToPvt
)
SELECT #Cmd += Cmd
FROM Final
INSERT INTO #Table
EXEC sp_executesql #Cmd
SELECT *
FROM #Table