JSON_MODIFY all values without looping - sql

See sample below. How would I use JSON_MODIFY or other way to modify all the "disc" values to "100" without having to update each array item in a loop?
create table #temp_data (json_text nvarchar(max))
insert into #temp_data select
'
"curr":"USD",
"items":[
{
"line":1,
"disc":10,
},
{
"line":2,
"disc":11
},
{
"line":3,
"disc":12,
}
]
}'
select * from #temp_data

We don't actually need to parse the rest of the JSON, we only need the $.items part. So we can APPLY that property with OPENJSON, then reassemble it with JSON_MODIFY:
UPDATE t
SET json_text = JSON_MODIFY(t.json_text, '$.items', v.items)
FROM temp_data t
CROSS APPLY (
SELECT line, disc = 100
FROM OPENJSON(t.json_text, '$.items') WITH (line int) AS items
FOR JSON PATH
) v(items);
In the WITH block, we need to add all properties we are not modifying, then in the inner SELECT we add any columns we want to change.

One way would be to use the traditional SQL DML UPDATE to replace the json_text with a new piece of JSON derived from the existing. Something like this
JSON_MODIFY could be added if that's a requirement.
drop table if exists #temp_data;
go
create table #temp_data (json_text nvarchar(max));
insert into #temp_data(json_text) values(
N'{
"curr":"USD",
"items":[
{
"line":1,
"disc":10
},
{
"line":2,
"disc":11
},
{
"line":3,
"disc":12
}
]
}');
update #temp_data
set json_text=( select json_value(d.json_text, N'strict $.curr') curr,
(select j2.line, j2.disc+100 as disc
from #temp_data td
cross apply openjson(td.json_text)
with(curr nvarchar(20),
items nvarchar(max) as json) j1
cross apply openjson(j1.items)
with(line int,
disc int) j2
for json path) items
from #temp_data d
for json path, without_array_wrapper);
select * from #temp_data;
{
"curr": "USD",
"items": [
{
"line": 1,
"disc": 210
},
{
"line": 2,
"disc": 211
},
{
"line": 3,
"disc": 212
}
]
}

It's not possible to use a wildcard in a JSON_MODIFY() call, so one possible approach are the following steps:
Parse the $.items array from the stored JSON using OPENJSON() and explicit schema including the line key.
Set the new value for the disc key for each item of this array.
Generate the JSON again using FOR JSON PATH.
Update the table with JSON_MODIFY().
Table:
create table #temp_data (json_text nvarchar(max))
insert into #temp_data (json_text)
VALUES (N'
{
"curr":"USD",
"items":[
{"line":1, "disc":10},
{"line":2, "disc":11},
{"line":3, "disc":12}
]
}')
Statement:
UPDATE #temp_data
SET json_text = JSON_MODIFY(
json_text,
'$.items',
(
SELECT line, 100 AS disc
FROM OPENJSON(json_text, '$.items') WITH (line int '$.line')
FOR JSON PATH
)
)
Result:
json_text
{
"curr":"USD",
"items":[{"line":1,"disc":100},{"line":2,"disc":100},{"line":3,"disc":100}]
}

Related

How to select a JSON array to a table

Is it possible to output a nested array into a table?
I intend to loop over the orders array and capture the line_item id's for the current iteration.
SQL
DECLARE #json NVARCHAR(4000) = N'
{
"orders":[
{
"id":123,
"line_items":[
{
"id":1
},
{
"id":2
}
]
}
]
};'
SELECT
[LineId]
FROM OPENJSON (#json, '$.orders.line_items') WITH (
[LineId] BIGINT '$.id'
);
Desired output
id
1
2
Here is a working option using a CROSS APPLY to extract the array.
SELECT id = JSON_VALUE(B.Value,'$.id')
FROM OPENJSON(#json, '$.orders') A
CROSS APPLY OPENJSON (A.value, '$.line_items') as B
Results
id
1
2

Using JSON_VALUE for parse column in SQL Server table

I have never worked with JSON in SQL Server before that's why need some help.
I have written a simple snippet of code:
DECLARE #json NVARCHAR(4000)
SET #json =
N'{
"id":"40476",
"tags":[
{
"id":"5f5883",
},
{
"id":"5fc8",
}
],
"type":"student",
"external_id":"40614476"
}'
SELECT
JSON_value(#json, '$.tags[0].id') as tags
In sample above I write code how get first "id" from "tags".
But how looks like script if in "tags" not 2 "id", but an unknown number this "id" and result should be in column like this:
1 5f5883
2 5fc8
You may use OPENJSON() with explicit schema to parse the $.tags JSON array:
DECLARE #json NVARCHAR(4000)
SET #json =
N'{
"id":"40476",
"tags":[
{
"id":"5f5883"
},
{
"id":"5fc8"
}
],
"type":"student",
"external_id":"40614476"
}'
SELECT id
FROM OPENJSON(#json, '$.tags') WITH (id varchar(10) '$.id')
Result:
id
------
5f5883
5fc8
If you want to get the index of each id in the $.tags JSON array, then you need a combination of OPENJSON() with default schema and JSON_VALUE():
SELECT CONVERT(int, [key]) AS rn, JSON_VALUE([value], '$.id') AS id
FROM OPENJSON(#json, '$.tags')
Result:
rn id
----------
0 5f5883
1 5fc8

Combining two tables result into JSON - SQL server

I am having two tables STUDENT and MASTER as follows
CREATE TABLE student(sid int,sname varchar(10),saddress varchar(30))
CREATE TABLE master(mid int,mname varchar(10),maddress varchar(30))
Now I want the JSON Structure as
{
"student": [
{
"sid": "value",
"sname": "value",
"saddress": "value"
}
],
"master": [
{
"mid": "value",
"mname": "value",
"maddress": "value"
}
]
}
In short I can say:
{
"table1":[],
"table2":[]
}
I have tried this but result is not as expected
SELECT * FROM student,master FOR JSON AUTO
Can any one help me to form the required JSON Structure from the query ??
Couple ideas:
SELECT student,
[master] --This is a poor choice for an object, as master is the name of a system database
FROM (VALUES((SELECT * FROM dbo.student FOR JSON AUTO),
(SELECT * FROM dbo.[master] FOR JSON AUTO)))V(student,[master])
FOR JSON AUTO;
SELECT (SELECT *
FROM dbo.student
FOR JSON AUTO) AS student,
(SELECT *
FROM dbo.[master] --This is a poor choice for an object, as master is the name of a system database
FOR JSON AUTO) AS [master]
FOR JSON PATH;

Create json key value from table column name and data

Is it possible to create JSON key value from a table SELECT statement, where column name as key and the column value as value
declare #T table(Id int, ItemName varchar(10), CategoryId int, ItemDate date)
insert into #T
values(1,'ABC',100, '1/1/2020')
to return something as below
{
"id": 1,
"table": "tableName",
"data": [{
"key": "ItemName",
"value": "ABC"
},
{
"key": "CategoryId",
"value": "100"
},
{
"key": "ItemDate",
"value": "1/1/2020"
}
]
}
I have looked at selecting as JSON but stuck here
select *
from #T
for json auto
You may try to use VALUES table value constructor and FOR JSON AUTO. As is mentioned in the documentation, when ... you specify the AUTO option, the format of the JSON output is automatically determined based on the order of columns in the SELECT list and their source tables.
Table:
CREATE TABLE Tbl (
Id int,
ItemName varchar(10),
CategoryId int,
ItemDate date
)
INSERT INTO Tbl
VALUES
(1, 'ABC', 100, '1/1/2020'),
(2, 'DEF', 200, '2/2/2020')
Statement:
SELECT t.Id, data.[key], data.[value]
FROM Tbl t
CROSS APPLY (VALUES
('ItemName', CONVERT(varchar(max), ItemName)),
('CategoryId', CONVERT(varchar(max), CategoryId)),
('ItemDate', CONVERT(varchar(max), ItemDate))
) Data ([key], [value])
FOR JSON AUTO
Result:
[
{
"Id":1,
"Data":[
{"key":"ItemName", "value":"ABC"},
{"key":"CategoryId","value":"100"},
{"key":"ItemDate","value":"2020-01-01"}
]
},
{
"Id":2,
"Data":[
{"key":"ItemName", "value":"DEF"},
{"key":"CategoryId", "value":"200"},
{"key":"ItemDate", "value":"2020-02-02"}
]
}
]
As an additional option you may try to build the inner JSON for each row:
SELECT
Id,
(
SELECT [key], [value]
FROM (VALUES
('ItemName', CONVERT(varchar(max), ItemName)),
('CategoryId', CONVERT(varchar(max), CategoryId)),
('ItemDate', CONVERT(varchar(max), ItemDate))
) v ([key], [value])
FOR JSON PATH
) AS Data
FROM Tbl
FOR JSON AUTO

How to write a select query to get the index value from Json object

I have the below JSON object. I need to write a select query to get the index values of Object JSON array. Kind of getting the sequence value.
{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC",
},
{
"ID": 11,
"Name": "ABCD",
},
{
"ID": 15,
"Name": "ABCDE",
},
]
}]}
Expected Output:
Index_Value
1
2
3
If I understand the question correctly and you want to get the index of the items in the Object JSON array, you need to use OPENJSON() with default schema. The result is a table with columns key, value and type and in case of JSON array, the key column holds the index of each item in the array (0-based):
JSON:
DECLARE #json nvarchar(max) = N'{
"Model":[
{
"ModelName":"Test Model",
"Object":[
{
"ID":1,
"Name":"ABC"
},
{
"ID":11,
"Name":"ABCD"
},
{
"ID":15,
"Name":"ABCDE"
}
]
}
]
}'
Statement:
SELECT CONVERT(int, j2.[key]) + 1 AS item_id
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') j2
But if you want to get the values of the ID keys in the Object JSON array, the statement is different:
SELECT j2.ID
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') WITH (
ID int '$.ID'
) j2
Note, that you need two OPENJSON() calls, because the input JSON has nested array structure. Of course, if Model JSON array has always one item, you may simplify the statement using an appropriate path:
SELECT CONVERT(int, [key]) + 1 AS item_id
FROM OPENJSON (#json, '$.Model[0].Object')
Finally, to get index, ID and Name, you should use the following statement, which assumes, that $.Model JSON array has more than one item and defines ID and Name columns with the appropraite data types:
SELECT
CONVERT(int, j2.[key]) + 1 AS ItemID,
j3.ID, j3.Name
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') j2
CROSS APPLY OPENJSON(j2.[value], '$') WITH (
ID int '$.ID',
Name varchar(50) '$.Name'
) j3
DECLARE #json nvarchar(max) = N'{
"Model":[
{
"ModelName":"Test Model",
"Object":[
{
"ID":1,
"Name":"ABC"
},
{
"ID":11,
"Name":"ABCD"
},
{
"ID":15,
"Name":"ABCDE"
}
]
}
]
}'
declare #i int=0;
SELECT
j2.ID, j2.Name
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value],concat('$.Object[',#i,']')) WITH (
ID i`enter code here`nt '$.ID', Name varchar(100) '$.Name'
) j2
Results:-
ID
Name
11
ABCD
you can select the key columns in select clause no need to mention in with of crossjoin.
SELECT
distinct t.id,
JSON_VALUE(AttsData.[value], '$.address') as address,
JSON_VALUE(AttsData.[value], '$.name') as name,
JSON_VALUE(AttsData.[value], '$.owner_name') as owner_name,
JSON_VALUE(AttsData.[value], '$.project') as project
,CONVERT(int, AttsData.[key]) index_id
FROM mytablewithjsonfeild t
CROSS APPLY OPENJSON (t."jsonfeild",N'$.parentkey') as AttsData
Above query, from the table I have cross joined the JSON field. and in select statement i have taken the specific keys.
and CONVERT(int, AttsData.[key]) to get the index of the elements