Modifying multiple JSON array elements in SQL Server 2017 - sql

I have a SQL Server 2017 table Orders which has an OrderId primary key and nvarchar(max) column Details. This column contains a json string which represents an array of "items". Here is a sample:
{ items[
{
"id": 1,
"isDeleted": false
},
{
"id": 2,
"isDeleted": false
},
{
"id": 3,
"isDeleted": false
},
{
"id": 4,
"isDeleted": false
}
] }
I am trying to figure out if there is a way to have a single (or few) SQL statement which will allow me to update one or more of the isDeleted attributes in the Details column of this table, given an OrderId for the record in the table and also a list of Ids in the Details column to update.
So for instance, I would like to update Ids 2 and 3 to be true in the Details JSON string record for a given OrderId. I know I can do this in a while loop and using json_modify, but I am wondering if there is a more elegant solution with some combination of json_modify, json_query or openjson.
Thanks in advance for any suggestions.

You may use one of the following approaches:
Parse the Details JSON for each OrderId uisng OPENJSON() and explicit schema. The result is a table with columns, defined in the WITH clause. Update this table and return the changed data as JSON again using FOR JSON.
Parse the Details JSON for each OrderId uisng OPENJSON() and default schema. The result is a table with columns key, value and type and one row for each item (JSON object) in the items JSON array. Update this table and generate the items JSON array with string-based approach (I don't think that FOR JSON can generate an array of scalar values / JSON objects). Update the JSON in the source table with JSON_MODIFY().
Generate and execute a dynamic statement using JSON_MODIFY()
Table with data:
CREATE TABLE Orders (OrderId int, Details nvarchar(max))
INSERT INTO Orders (OrderId, Details)
VALUES
(1, N'{"items":[{"id":1,"isDeleted":false},{"id":2,"isDeleted":false},{"id":3,"isDeleted":false},{"id":4,"isDeleted":false}]}'),
(2, N'{"items":[{"id":11,"isDeleted":false},{"id":12,"isDeleted":false},{"id":13,"isDeleted":false}]}')
Table with IDs:
CREATE TABLE ItemIds (id int)
INSERT INTO ItemIds (id) VALUES (1), (3)
Statement with OPENJSON() and explicit schema:
UPDATE Orders
SET Details = (
SELECT
j.id AS id,
CONVERT(bit, CASE WHEN i.id IS NOT NULL THEN 1 ELSE j.isDeleted END) AS isDeleted
FROM OPENJSON(Details, '$.items') WITH (
id int '$.id',
isDeleted bit '$.isDeleted'
) j
LEFT OUTER JOIN ItemIds i ON j.id = i.id
FOR JSON AUTO, ROOT('Items')
)
WHERE OrderId = 1
Statement with OPENJSON() and default schema:
UPDATE Orders
SET Details = JSON_MODIFY(
Details,
'$.items',
JSON_QUERY((
SELECT CONCAT(
'[',
STRING_AGG(
CASE
WHEN i.id IS NULL THEN j.[value]
ELSE JSON_MODIFY(j.[value], '$.isDeleted', CONVERT(bit, 1))
END,
','
),
']'
)
FROM OPENJSON(Details, '$.items') j
LEFT OUTER JOIN ItemIds i ON CONVERT(int, JSON_VALUE(j.[value], '$.id')) = i.id
))
)
WHERE OrderId = 1
Dynamic statement:
DECLARE #stm nvarchar(max)
SELECT #stm = STRING_AGG(
CONCAT(
'UPDATE Orders ',
'SET Details = JSON_MODIFY(Details, ''$.items[', a.[key], '].isDeleted'', CONVERT(bit, 1)) ',
'WHERE OrderId = ', o.OrderId, ';'
),
' '
)
FROM Orders o
CROSS APPLY (
SELECT o.OrderId, j1.[key]
FROM OPENJSON(o.Details, '$.items') j1
CROSS APPLY OPENJSON(j1.[value]) WITH (id int '$.id') j2
WHERE j2.id IN (SELECT id FROM ItemIds)
) a
WHERE o.OrderId = 1
PRINT #stm
EXEC sp_executesql #stm
Result:
OrderId Details
1 {"items":[{"id":1,"isDeleted":true},{"id":2,"isDeleted":false},{"id":3,"isDeleted":true},{"id":4,"isDeleted":false}]}
2 {"items":[{"id":11,"isDeleted":false},{"id":12,"isDeleted":false},{"id":13,"isDeleted":false}]}

SQL Server is perfectly capable of performing such operation. It is another question if this is good design though.
This is just a demo and not production ready code, so there is a lot of space for improvement:
-- param section
DECLARE #OrderId INT = 1;
DECLARE #t TABLE(id INT, new_val NVARCHAR(10));
INSERT INTO #t(id, new_val) VALUES(1, 'true'),(3, 'true');
--- single query
WITH cte AS (
SELECT o.*,
s.[key],
JSON_VALUE(s.value, '$.id') AS id,
JSON_VALUE(s.value, '$.isDeleted') AS isDeleted
FROM Orders o
CROSS APPLY OPENJSON(o.Details ,N'$.items') s
WHERE o.OrderId = #OrderId
), cte_new AS (
SELECT DISTINCT c.OrderId, c.Details, s.Details_new
FROM cte c
CROSS APPLY (
SELECT c2.id, isDeleted = COALESCE(t.new_val, c2.IsDeleted)
FROM cte c2
LEFT JOIN #t t
ON c2.id = t.id
WHERE c2.OrderId = c.OrderId
FOR JSON AUTO) s(Details_new)
)
UPDATE o
SET Details = cn.Details_new
FROM Orders o
JOIN cte_new cn
ON o.OrderId = cn.OrderId;
db<>fiddle demo
How it works:
Parse JSON to tabular format
Perform data manipulation(here using #t as parameter)
Aggregate back to JSON
Perform UPDATE

I don't have the right version of SQL Server to test out this code. But, you should be able to query and modify the data and generate a new json string.
DECLARE #json nvarchar(max) = '{"items" : [{"id": 1, "isDeleted": false}, {"id": 2, "isDeleted": false}, {"id": 3, "isDeleted": false}, {"id": 4, "isDeleted": false}]}'
SELECT *
FROM OPENJSON(#json)
WITH (id int '$.items.id', isDeleted bit '$.items.isDeleted')

Related

Get Array Index from JSON data SQL Server

{
"Name": ["dokumen_1","dokumen_2","dokumen_3","dokumen_4"],
"Date": [0,0,0,0],
"Progress": [0,0,0,0]
}
I want to fetch Date and Progress value according to Name position.
I changed the date and progress values for a better illustration
NOTE: in 2016 the JSON_VALUE has to be a literal
Example
Declare #JSON varchar(max) = '
{
"Name":["dokumen_1","dokumen_2","dokumen_3","dokumen_4"],
"Date":[1,2,3,4],
"Progress":[11,12,13,12]
}'
Select Name = value
,Date = JSON_VALUE(#JSON,'$.Date['+[key]+']')
,Progress = JSON_VALUE(#JSON,'$.Progress['+[key]+']')
From openjson(#JSON,N'$.Name')
Results
Name Date Progress
dokumen_1 1 11
dokumen_2 2 12
dokumen_3 3 13
dokumen_4 4 12
Another possible approach is a combination of OPENJSON() with default schema and appropriate JOINs. Of course, you need at least SQL Server 2016 to use the built-in JSON support.
DECLARE #json varchar(max) = '
{
"Name":["dokumen_1","dokumen_2","dokumen_3","dokumen_4"],
"Date":[101,102,103,104],
"Progress":[201,202,203,204]
}'
SELECT n.[value] AS Name, d.[value] AS Date, p.[value] AS Progress
FROM OPENJSON(#json, '$.Name') n
LEFT JOIN OPENJSON(#json, '$.Date') d ON n.[key] = d.[key]
LEFT JOIN OPENJSON(#json, '$.Progress') p ON n.[key] = p.[key]

Update json in same row multiple times with for different JSON paths

I need to update a JSON data present in a row, with multiple updates to the same row. Below is the kind of json
{
"secondaries": [
{
"secondaryId": 1,
"primaries": [
{
"primary": 1,
"status": "UNKNOWN"
},
{
"primary": 2,
"status": "UNKNOWN"
}
]
}
]
}
CREATE TABLE testing(
id VARCHAR(100),
json nvarchar(max)
);
INSERT INTO testing values('123', '{"secondaries":[{"secondaryId":1,"primaries":[{"primary":1,"status":"UNKNOWN"},{"primary":2,"status":"UNKNOWN"}]}]}');
I want to update status for all the primary as PASSED. So I first created a CTE
with cte as (select id,
CONCAT('$.secondaries[', t.[key], ']', '.primaries[', t2.[key],
']') as primaryPath
from testing
cross apply openjson(json, '$.secondaries') as t
cross apply openjson(t.value, '$.primaries') as t2
where id = '123'
and json_value(t.value, '$.secondaryId') = 1
)
select * from cte;
This gives me below results
Now if I try and update the records using below sql query:
with cte as (select id,
CONCAT('$.secondaries[', t.[key], ']', '.primaries[', t2.[key],
']') as primaryPath
from testing
cross apply openjson(json, '$.secondaries') as t
cross apply openjson(t.value, '$.primaries') as t2
where id = '123'
and json_value(t.value, '$.secondaryId') = 1
)
update testing
set json = JSON_MODIFY(json, cte.primaryPath + '.status', 'PASSED')
from testing
cross join cte
where cte.id = testing.id;
select * from testing;
Only one of the records gets updated. I want all the records to get update. How can I achieve the same?
http://sqlfiddle.com/#!18/b61e1/6
I do have a working solution to do it, but it is not a query based one. I am looking for a possibility to do it just via the query itself
OPEN #getid
FETCH NEXT
FROM #getid INTO #id, #primaryPath
WHILE ##FETCH_STATUS = 0
BEGIN
update testing
set json = JSON_MODIFY(json, #primaryPath + '.status', 'PASSED')
where testing.id = #id
FETCH NEXT
FROM #getid INTO #id, #primaryPath
END
CLOSE #getid
DEALLOCATE #getid
If you don't want to rebuild the whole JSON, you can use nested JSON_MODiFY calls.
It gets more complicated because of the doubly nested arrays, you need to use STRING_AGG also, and JSON_QUERY to prevent double-escaping.
UPDATE t
SET json = JSON_MODIFY(t.json, '$.secondaries', JSON_QUERY(j1.secondaries_new))
FROM testing t
CROSS APPLY (
SELECT '[' + STRING_AGG(JSON_MODIFY(secondaries.value, '$.primaries', JSON_QUERY(j2.primaries_new)), ',') + ']'
FROM OPENJSON(t.json, '$.secondaries') secondaries
CROSS APPLY (
SELECT '[' + STRING_AGG(JSON_MODIFY(primaries.value, '$.status', 'PASSED'), ',') + ']'
FROM OPENJSON(secondaries.value, '$.primaries') primaries
) j2(primaries_new)
) j1(secondaries_new);
db<>fiddle
The JSON_MODIY() function doesn't support wild cards for the path parameter, so possible options (...query based...) are:
Parse, modify and build the JSON content for each row (this approach expects a known and fixed JSON structure).
Generate and execute a dynamic statement with nested JSON_MODIFY() calls.
Parse, modify and build the JSON content for each row:
DECLARE #id varchar(100) = '123'
DECLARE #secondaryId int = 1
UPDATE testing
SET json = (
SELECT
secondaryId,
primaries = CASE
WHEN secondaryId = #secondaryId THEN
(
SELECT [primary], 'PASSED' AS status
FROM OPENJSON(_primaries) WITH ([primary] int '$.primary')
FOR JSON PATH
)
ELSE JSON_QUERY(_primaries)
END
FROM OPENJSON(json, '$.secondaries') WITH (
secondaryId int '$.secondaryId',
_primaries nvarchar(max) '$.primaries' AS JSON
)
FOR JSON PATH, ROOT('secondaries')
)
WHERE (id = #id)
Dynamic statement:
DECLARE #stmt nvarchar(max) = N''
DECLARE #id varchar(100) = '123'
DECLARE #secondaryId int = 1
; WITH cte AS (
SELECT
id,
CONCAT('$.secondaries[', j1.[key], ']', '.primaries[', j2.[key],']') AS primaryPath
FROM testing
CROSS APPLY OPENJSON(json, '$.secondaries') AS j1
CROSS APPLY OPENJSON(j1.[value], '$.primaries') AS j2
WHERE (id = #id) AND JSON_VALUE(j1.[value], '$.secondaryId') = #secondaryId
)
SELECT
#stmt = CONCAT(
N'UPDATE testing SET json = ',
STRING_AGG(N'JSON_MODIFY(', N''),
N'json',
STRING_AGG(CONCAT(N',''', primaryPath, N'.status'',''PASSED'')'), N''),
N' WHERE id = #id'
)
FROM cte
DECLARE #err int
EXEC #err = sp_executesql #stmt, N'#id varchar(100)', #id
IF #err = 0 PRINT 'Success' ELSE PRINT 'Error'

How to parse json data then save it as a variable in sql server

Will i am sql server to create json which i import using ASP.NET
My select function:
SELECT Sales.SalesID,InvoiceNum,FORMAT(InvoiceDate, 'dd-MMM-yy') AS InvoiceDate,ProductName,Qty,Price
FROM Sales
INNER JOIN (SalesDetails
INNER JOIN Products
ON Products.ProductID = SalesDetails.ProductID)
ON Sales.SalesID = SalesDetails.SalesID
FOR JSON AUTO
My JSON Output:
{
"SalesID":1,
"InvoiceNum":"111",
"InvoiceDate":"11-Feb-19",
"Products":[
{
"ProductName":"Name",
"SalesDetails":[
{
"Qty":5,
"Price":100
}
]
},
{
"ProductName":"Name",
"SalesDetails":[
{
"Qty":10,
"Price":210.00
}
]
}
]
}
My Desired Output:
{
"SalesID":1,
"InvoiceNum":"11",
"InvoiceDate":"11-Feb-19",
"Products":[
{
"ProductName":"Name",
"Qty":5,
"Price":100
},
{
"ProductName":"Name",
"Qty":10,
"Price":210
}
]
}
I want to save this as a table with OpenJson After outputing this
My details query is:
My attempt with JSON PATH:
Select:
DECLARE #json NVARCHAR(MAX);
SET #json = (SELECT Sales.SalesID,InvoiceNum,FORMAT(InvoiceDate, 'dd-MMM-yy') AS InvoiceDate,ProductName AS "Products.Name",Qty AS "Products.Qty",Price AS "Products.Price"
FROM Sales
INNER JOIN (SalesDetails
INNER JOIN Products
ON Products.ProductID = SalesDetails.ProductID)
ON Sales.SalesID = SalesDetails.SalesID
FOR JSON PATH
SELECT * FROM OPENJSON(#json)
WITH (
SalesID INT 'strict $.SalesID',
InvoiceNum NVARCHAR(50) '$.InvoiceNum',
InvoiceDate NVARCHAR(9) '$.InvoiceDate',
Products NVARCHAR(MAX) '$.Products' AS JSON
)
My output:
Any help is appreciated.
You may try to generate the expected output using FOR JSON PATH. With FOR JSON AUTO, the format of the JSON output is automatically determined based on the order of columns in the SELECT list and their source tables and this format can't be changed.
It's difficult without test data, but the following statement is a possible solution to your problem:
DECLARE #json nvarchar(max);
SELECT #json = (
SELECT
s.SalesID,
s.InvoiceNum,
FORMAT(s.InvoiceDate, 'dd-MMM-yy') AS InvoiceDate,
c.Products
FROM Sales s
CROSS APPLY (
SELECT p.ProductName, sd.Qty, sd.Price
FROM SalesDetails sd
INNER JOIN Products p ON p.ProductID = sd.ProductID
WHERE sd.SalesID = s.SalesID
FOR JSON PATH
) c (Products)
FOR JSON PATH
)
After that you may try to parse the generated JSON:
SELECT * FROM OPENJSON(#json) WITH (
SalesID INT 'strict $.SalesID',
InvoiceNum NVARCHAR(50) '$.InvoiceNum',
InvoiceDate NVARCHAR(9) '$.InvoiceDate',
Products NVARCHAR(MAX) '$.Products' AS JSON
)

Add range to an array with JSON_MODIFY

I am trying to add an array to another array using JSON_MODIFY.
The situation is, I have an array kind stored json data in database. It looks like this:
declare #base nvarchar(max) = '[{"name":"base"}]';
And I am getting another set of data which is also in the shape of array:
declare #test1 nvarchar(max) = '[{"name":"test1"},{"name":"example1"}]';
I am trying to use JSON_MODIFY and JSON_QUERY magics to append them together but it gives me unexpected results.
declare #base nvarchar(max) = '[{"name":"base"}]';
declare #test1 nvarchar(max) = '[{"name":"test1"},{"name":"example1"}]';
set #base = JSON_MODIFY(#base,'append $',JSON_QUERY(#test1));
select #base;
Output:
[{"name":"base"}, [{"name":"test1"},{"name":"example1"}]]
But what I want is using those methods to make it work like kind of Add-Range:
[{"name":"base"},{"name":"test1"},{"name":"example1"}]
I am kind of lost on this process and I don't know where to look at for this kind of functionality.
I will use this from a C# service to directly modify through the code. That's why I cannot use Store procedures and functions as well.
Edit #1:
With regarding to reply from #Salman A, i appreciate your answer but the thing is, as i said earlier, it is a little bit difficult to use on my query running through code. Which is:
declare #test1 nvarchar(max) = '[{"name":"test1"},{"name":"example1"}]';
UPDATE dbo.ExampleTable
SET [Data] = JSON_MODIFY([Data], 'append $', JSON_QUERY(#test1))
WHERE [UniqueId] = 'some_guid_here'
I have tried it to adapt the answer that i like this :
declare #test1 nvarchar(max) = '[{"name":"test1"},{"name":"example1"}]';
UPDATE dbo.ExampleTable
SET [Data] = (
select [Data] = JSON_MODIFY([Data],'append $',item)
from OPENJSON(#test1)
with ([item] nvarchar(max) '$' as JSON)
)
WHERE [UniqueId] = 'some_id'
Actually, it works if #test1 only have 1 item, but in case of having more than 1 item in #test1, it gives the error:
Subquery returned more than 1 value. This is not permitted when the subquery follows = .....
What is the logical way to use this in a update set subquery
You can use OPENJSON to convert the array to rows and append items one by one:
declare #base nvarchar(max) = '[{"name":"base"}]';
declare #test1 nvarchar(max) = '[{"name":"test1"},{"name":"example1"}]';
select #base = json_modify(#base, 'append $', item)
from openjson(#test1)
with ([item] nvarchar(max) '$' as json);
select #base;
Returns:
[{"name":"base"},{"name":"test1"},{"name":"example1"}]
Revised answer for update query
If you're using SQL Server 2017+ then a reasonably safe solution is to concatenate the array using STRING_AGG but build individual rows using JSON functions. It is relatively easy to use this idea in an update query:
DECLARE #base NVARCHAR(MAX) = '[{"name":"base"}]';
DECLARE #test NVARCHAR(MAX) = '[{"foo":"bar"},{"baz":"meh"}]';
SELECT '[' + STRING_AGG(jsonstr, ',') WITHIN GROUP (ORDER BY pos) + ']'
FROM (
SELECT value, 1000 + [key] FROM OPENJSON(#base)
UNION ALL
SELECT value, 2000 + [key] FROM OPENJSON(#test)
) AS x(jsonstr, pos);
Alternately, you can use a recursive CTE that calls JSON_MODIFY multiple times to build the JSON; you can use the result in update query:
CREATE TABLE t(
id INT NOT NULL PRIMARY KEY IDENTITY,
data NVARCHAR(MAX)
);
INSERT INTO t(data) VALUES
('[{"name":"1.1"}]'),
('[{"name":"2.1"},{"name":"2.2"}]');
WITH rows(data, pos) AS (
SELECT value, [key]
FROM OPENJSON('[{"foo":"bar"},{"baz":"meh"}]')
), rcte(id, data, pos) AS (
SELECT id, data, -1
FROM t
UNION ALL
SELECT prev.id, JSON_MODIFY(prev.data, 'append $', JSON_QUERY(curr.data)), prev.pos + 1
FROM rcte AS prev
JOIN rows AS curr ON curr.pos = prev.pos + 1
)
UPDATE t
SET data = (
SELECT TOP 1 data
FROM rcte
WHERE id = t.id
ORDER BY pos DESC
);
Demo on db<>fiddle

Updating a json array IN SQL Server table

I have an array of json in a SQL Server column, I am trying to update all names to 'Joe'.
I tried the below code , but it is updating only first element of the json array
CREATE TABLE #t (I INT, JsonColumn NVARCHAR(MAX) CHECK (ISJSON(JsonColumn) > 0))
INSERT INTO #t
VALUES (1, '[{"id":"101","name":"John"}, {"id":"102","name":"peter"}]')
INSERT INTO #t VALUES (2,'[{"id":"103","name":"dave"}, {"id":"104","name":"mark"}]')
SELECT * FROM #t
SELECT * FROM #t
CROSS APPLY OPENJSON(JsonColumn) s
WITH cte AS
(
SELECT *
FROM #t
CROSS APPLY OPENJSON(JsonColumn) s
)
UPDATE cte
SET JsonColumn = JSON_MODIFY(JsonColumn, '$[' + cte.[key] + '].name', 'Joe')
SELECT * FROM #t
-- DROP TABLE #t
It is only updating the first element of array to joe
Current result:
[{"id":"101","name":"Joe"}, {"id":"102","name":"cd"}]
[{"id":"103","name":"Joe"}, {"id":"104","name":"mark"}]
Expected
[{"id":"101","name":"Joe"}, {"id":"102","name":"Joe"}]
[{"id":"103","name":"Joe"}, {"id":"104","name":"Joe"}]
Since you want to do in one transaction, I could not think of any other ways than to create another table and store the values into new table and use for XML path with the value. Problem is you are trying to update JSON array and I am not sure how would you update the same row twice with different value. With cross apply as you have shown it creates two rows and then only you can update it to JOE.
Your query will update name = Joe for ID = 101 for first row, and Name = Joe for ID = 102 based on value column. Since these are on two different rows you are seeing only one change in your temp table.
I created one more #temp2 table to store those values and use XML path to concatenate. The final table will be #t2 table for your expected results.
SELECT *
into #t2
FROM #t
CROSS APPLY OPENJSON(JsonColumn) s
select *, json_value (value, '$.name') from #t2
UPDATE #t2
SET value = JSON_MODIFY(value, '$.name', 'Joe')
select t.I ,
JSONValue = concat('[',stuff((select ',' + value from #t2 t1
where t1.i = t.i
for XML path('')),1,1,''),']')
from #t2 t
group by t.I
Output:
I JSONValue
1 [{"id":"101","name":"Joe"},{"id":"102","name":"Joe"}]
Updating original table:
update t
set t.JsonColumn =t2.JSONValue
from #t t
join (select t.I ,
JSONValue = concat('[',stuff((select ',' + value from #t2 t1
where t1.i = t.i
for XML path('')),1,1,''),']')
from #t2 t
group by t.I ) t2 on t.I = t2.i
I think that it is impossible to apply more updates to one record with one command. So you need to explode JSON array to records.
You can do this with a Temporary or Variable Table and a Cursor.
-- Declare the Variable Table
DECLARE #JsonTable TABLE (
RecordKey UNIQUEIDENTIFIER,
ArrayIndex INT,
ObjKey NVARCHAR(100),
ObjValue NVARCHAR(1000)
);
-- Fill the Variable Table
INSERT INTO #JsonTable
SELECT TB1.pk as RecordKey,
TB1data.[key] AS ArrayIndex,
TB1dataItem.[key] as ObjKey,
TB1dataItem.[value] as ObjValue
FROM MyTable TB1
CROSS APPLY OPENJSON(JSON_QUERY(TB1.data, '$.list')) TB1data
CROSS APPLY OPENJSON(JSON_QUERY(TB1data.value, '$')) TB1dataItem
WHERE TB1dataItem.[key] = 'name'
-- Declare Cursor and relative variables
DECLARE #recordKey UNIQUEIDENTIFIER,
#recordData NVARCHAR(MAX),
#arrayIndex INT,
#objKey NVARCHAR(100),
#objValue NVARCHAR(1000);
DECLARE JsonCursor CURSOR FAST_FORWARD READ_ONLY FOR
SELECT * FROM #JsonTable;
-- Use Cursor to read any json array item
OPEN JsonCursor;
FETCH NEXT
FROM JsonCursor
INTO #recordKey, #arrayIndex, #objKey, #objValue;
WHILE ##FETCH_STATUS = 0 BEGIN
UPDATE TB1
SET data = JSON_MODIFY(
data,
'$.list[' + CAST(#arrayIndex as VARCHAR(20)) + '].name',
'Joe'
)
FROM MyTable TB1
WHERE TB1.pk = #recordKey;
FETCH NEXT
FROM JsonCursor
INTO #recordKey, #arrayIndex, #objKey, #objValue;
END;
CLOSE JsonCursor;
DEALLOCATE JsonCursor;
Do you need this?
CREATE TABLE #t (
I INT,
JsonColumn NVARCHAR(MAX) CHECK (ISJSON(JsonColumn) > 0)
);
INSERT INTO #t
VALUES (1, '[{"id":"101","name":"John"}, {"id":"102","name":"peter"}]');
INSERT INTO #t
VALUES (2, '[{"id":"103","name":"dave"}, {"id":"104","name":"mark"}]');
SELECT CONCAT('[', STRING_AGG(JSON_MODIFY(JSON_MODIFY('{}', '$.id', j.id), '$.name', 'John'), ','), ']')
FROM #t t
CROSS APPLY OPENJSON(JsonColumn) WITH (id INT, name sysname) j
GROUP BY t.I