Remove elements from a JSON array, with no key - json

I'm looking for a way that I can remove elements from a JSON array in SQL Server, I tried using JSON_MODIFY/OPENPATH, but I can't seem to get the path parameter correct.
Here is an example of what I want to do is convert:
[{"FieldName":"OrderStatusTypeId","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2},{"FieldName":"PatientReferenceNumber","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2}]
To be:
[{"FieldName":"OrderStatusTypeId","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2}]
Basically, I want to remove any element of the array, whereby the Action is 2 and the ValueBefore and the ValueAfter fields are the same.
Here is what I'm attempting at the moment, as a test, but I keep getting the error below:
DECLARE #JSONData AS NVARCHAR(4000)
SET #JSONData = N'[{"FieldName":"OrderStatusTypeId","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2},{"FieldName":"PatientReferenceNumber","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"PoNumber","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"ReferringPhysicianName","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"InsuranceProvider","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"TreatmentId","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"RowVersion[6]","FieldType":"Byte","ValueBefore":"10","ValueAfter":"115","Action":2},{"FieldName":"OrderStatusType.Id","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2},{"FieldName":"OrderStatusType.Description","FieldType":"String","ValueBefore":"Delivered","ValueAfter":"Failed Logistics","Action":2},{"FieldName":"OrderStatusType.IsSelectable","FieldType":"Boolean","ValueBefore":"False","ValueAfter":"True","Action":2}]'
SELECT #JSONData = JSON_MODIFY(#JSONData, '$',
JSON_QUERY(
(
SELECT *
FROM OPENJSON(#JSONData, '$') WITH (
FieldName nvarchar(1000) '$.FieldName',
FieldType nvarchar(1000) '$.FieldType',
ValueBefore nvarchar(1000) '$.ValueBefore',
ValueAfter nvarchar(1000) '$.ValueAfter',
Action int '$.Action'
)
WHERE Action <> 2 AND ValueBefore <> ValueAfter
FOR JSON PATH
)
)
)
Error:
Msg 13619, Level 16, State 1, Line 4
Unsupported JSON path found in argument 2 of JSON_MODIFY.
The expected output I'm looking for is:
[{"FieldName":"OrderStatusTypeId","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2},{"FieldName":"RowVersion[6]","FieldType":"Byte","ValueBefore":"10","ValueAfter":"115","Action":2},{"FieldName":"OrderStatusType.Id","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2},{"FieldName":"OrderStatusType.Description","FieldType":"String","ValueBefore":"Delivered","ValueAfter":"Failed Logistics","Action":2},{"FieldName":"OrderStatusType.IsSelectable","FieldType":"Boolean","ValueBefore":"False","ValueAfter":"True","Action":2}]
How can I work out the correct JSON Path value, all the examples I seem to find online don't have an array as the root element of the JSON string.
Note, the order of the elements isn't important.

You don't have to use JSON_MODIFY here. You can just select the data you want as a table, filter it, then re-encode it as JSON.
DECLARE #JSONData AS NVARCHAR(4000)
SET #JSONData = N'[{"FieldName":"OrderStatusTypeId","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2},{"FieldName":"PatientReferenceNumber","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"PoNumber","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"ReferringPhysicianName","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"InsuranceProvider","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"TreatmentId","FieldType":"String","ValueBefore":"","ValueAfter":"","Action":2},{"FieldName":"RowVersion[6]","FieldType":"Byte","ValueBefore":"10","ValueAfter":"115","Action":2},{"FieldName":"OrderStatusType.Id","FieldType":"Int32","ValueBefore":"8","ValueAfter":"10","Action":2},{"FieldName":"OrderStatusType.Description","FieldType":"String","ValueBefore":"Delivered","ValueAfter":"Failed Logistics","Action":2},{"FieldName":"OrderStatusType.IsSelectable","FieldType":"Boolean","ValueBefore":"False","ValueAfter":"True","Action":2}]'
set #JSONData =
(
SELECT *
FROM OPENJSON(#JSONData, '$') WITH (
FieldName nvarchar(1000) '$.FieldName',
FieldType nvarchar(1000) '$.FieldType',
ValueBefore nvarchar(1000) '$.ValueBefore',
ValueAfter nvarchar(1000) '$.ValueAfter',
Action int '$.Action'
)
WHERE not (Action = 2 and ValueBefore = ValueAfter)
FOR JSON PATH
)
and the JSON is
[
{
"FieldName": "OrderStatusTypeId",
"FieldType": "Int32",
"ValueBefore": "8",
"ValueAfter": "10",
"Action": 2
},
{
"FieldName": "RowVersion[6]",
"FieldType": "Byte",
"ValueBefore": "10",
"ValueAfter": "115",
"Action": 2
},
{
"FieldName": "OrderStatusType.Id",
"FieldType": "Int32",
"ValueBefore": "8",
"ValueAfter": "10",
"Action": 2
},
{
"FieldName": "OrderStatusType.Description",
"FieldType": "String",
"ValueBefore": "Delivered",
"ValueAfter": "Failed Logistics",
"Action": 2
},
{
"FieldName": "OrderStatusType.IsSelectable",
"FieldType": "Boolean",
"ValueBefore": "False",
"ValueAfter": "True",
"Action": 2
}
]

Related

Conditionally update JSON column

I have a table which has ID & JSON columns. ID is auto incrementing column. Here are my sample data.
Row 1
1 | {
"HeaderInfo":
{
"Name": "ABC",
"Period": "2010",
"Code": "123"
},
"HData":
[
{ "ID1": "1", "Value": "$1.00", "Code": "A", "Desc": "asdf" },
{ "ID1": "2", "Value": "$1.00", "Code": "B", "Desc": "pqr" },
{ "ID1": "3", "Value": "$1.00", "Code": "C", "Desc": "xyz" }
]
}
Row 2
2 | {
"HeaderInfo":
{
"Name": "ABC",
"Period": "2010",
"Code": "123"
},
"HData":
[
{ "ID1": "76", "Value": "$1.00", "Code": "X", "Desc": "asdf" },
{ "ID1": "25", "Value": "$1.00", "Code": "Y", "Desc": "pqr" },
{ "ID1": "52", "Value": "$1.00", "Code": "Z", "Desc": "lmno" },
{ "ID1": "52", "Value": "$1.00", "Code": "B", "Desc": "xyz" }
]
}
and it keep goes. Items inside the HData section is infinite. It can be any numbers of items.
On this JSON I need to update the Value = "$2.00" where "Code" is "B". I should be able to do this with 2 scenarios. My parameter inputs are #id=2, #code="B", #value="$2.00". #id sometimes will be null. So,
If #id is null then the update statement should go through all records and update the Value="$2.00" for all items inside the HData section which has Code="B".
If #id = 2 then the update statement should update only the second row which Id is 2 for the items which Code="b"
Appreciate your help in advance.
Thanks
See DB Fiddle for an example.
declare #id bigint = 2
, #code nvarchar(8) = 'B'
, #value nvarchar(8) = '$2.00'
update a
set json = JSON_MODIFY(json, '$.HData[' + HData.[key] + '].Value', #value)
from so75416277 a
CROSS APPLY OPENJSON (json, '$.HData') HData
CROSS APPLY OPENJSON (HData.Value, '$')
WITH (
ID1 bigint
, Value nvarchar(8)
, Code nvarchar(8)
, [Desc] nvarchar(8)
) as HDataItem
WHERE id = #id
AND HDataItem.Code = #Code
The update / set statement says we want to replace the value of json with a new generated value / functions exactly the same as it would in any other context; e.g. update a set json = 'something' from so75416277 a where a.column = 'some condition'
The JSON_MODIFY does the manipulation of our json.
The first input is the original json field's value
The second is the path to the value to be updated.
The third is the new value
'$.HData[' + HData.[key] + '].Value' says we go from our JSON's root ($), find the HData field, filter the array of values for the one we're after (i.e. key here is the array item's index), then use the Value field of this item.
key is a special term; where we don't have a WITH block accompanying our OPENJSON statement we get back 3 items: key, value and type; key being the identifier, value being the content, and type saying what sort of content that is.
CROSS APPLY allows us to perform logic on a value from a single DB rowto return potentially multiple rows; e.g. like a join but against its own contents.
OPENJSON (json, '$.HData') HData says to extract the HData field from our json column, and return this with the table alias HData; as we've not included a WITH, this HData column has 3 fields; key, value, and type, as mentioned above (this is the same key we used in our JSONMODIFY).
The next OPENJSON works on HData.Value; i.e. the contents of the array item under HData. Here we take the object from this array (i.e. that's the root from the current context; hence $), and use WITH to parse it into a specific structure; i.e. ID1, Value, Code, and Desc (brackets around Desc as it's a keyword). We give this the alias HDataItem.
Finally we filter for the bit of the data we're interested in; i.e. on id to get the row we want to update, then on HDataItem.Code so we only update those array items with code 'B'.
Try the below SP.
CREATE PROC usp_update_75416277
(
#id Int = null,
#code Varchar(15),
#value Varchar(15)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLStr Varchar(MAX)=''
;WITH CTE
AS
( SELECT ROW_NUMBER()OVER(PARTITION BY YourTable.Json ORDER BY (SELECT NULL))RowNo,*
FROM YourTable
CROSS APPLY OPENJSON(YourTable.Json,'$.HData')
WITH (
ID1 Int '$.ID1',
Value Varchar(20) '$.Value',
Code Varchar(20) '$.Code',
[Desc] Varchar(20) '$.Desc'
) HData
WHERE (#id IS NULL OR ID =#id)
)
SELECT #SQLStr=#SQLStr+' UPDATE YourTable
SET [JSON]=JSON_MODIFY(YourTable.Json,
''$.HData['+CONVERT(VARCHAR(15),RowNo-1)+'].Value'',
'''+CONVERT(VARCHAR(MAX),#value)+''') '+
'WHERE ID ='+CONVERT(Varchar(15),CTE.ID) +' '
FROM CTE
WHERE Code=#code
AND (#id IS NULL OR ID =#id)
EXEC( #SQLStr)
END

Parse JSON document into table

I'm working on parsing JSON document with an object array into a SQL Server table. I'm stuck with how to parse the complex object array.
I tried subproject.id and that did not work. I usually use the below code to parse a JSON array which works just fine but in this case it does not.
JSON:
{
"Data": [
{
"name": "ABC",
"date": "2020-01-20",
"subproject": [
{
"id": "123",
"projectname": "new1",
"refnum": "123:new1"
},
{
"id": "456",
"projectname": "new2",
"refnum": "456:new2"
}
],
"projectid": "1234",
"projectdate": "2020-01-27"
},
{
"name": "DEF",
"date": "2020-01-30",
"subproject": [
{
"id": "789",
"projectname": "new3",
"refnum": "789:new3"
},
{
"id": "901",
"projectname": "new4",
"refnum": "901:new4"
}
],
"projectid": "4567",
"projectdate": "2020-02-07"
}
]
}
SQL:
DECLARE #JSON VARCHAR(MAX)
SELECT #JSON = BulkColumn
FROM OPENROWSET (BULK 'c:/data/project.json', SINGLE_CLOB) X
SELECT *
FROM OPENJSON (#JSON)
WITH (
[name] NVARCHAR(256),
[date] DATE,
[projectid] INT,
[projectdate] DATE
)
You need to use AS JSON clause in the first OPENJSON() call (to specify that the $.subproject property contains an inner JSON array) and a combination of another OPENJSON() call and APPLY operator:
SELECT *
FROM OPENJSON (#JSON, '$.Data') WITH (
[name] NVARCHAR(256),
[date] DATE,
[projectid] INT,
[projectdate] DATE,
[subproject] NVARCHAR(MAX) '$.subproject' AS JSON
) j1
OUTER APPLY OPENJSON (j1.[subproject]) WITH (
[subprojectid] INT '$.id',
[subprojectname] NVARCHAR(256) '$.projectname',
[subprojectrefnum] NVARCHAR(256) '$.refnum'
) j2

MS SQL Json array processing with separate arrays

I have a problem with JSON data and how to get it in SQL table so the data is:
[
{
"id":"1121131",
"idGroups":[
"123",
"X999"
],
"idGroupNames":[
"Neutral",
"Service"
]
},
{
"id":"2233154",
"idGroups":[
"654321"
],
"idGroupNames":[
"Position"
]
}
]
and the desired output is
Id
idGroups
idGroupNames
1121131
123
Neutral
1121131
X999
Service
2233154
654321
Position
I have trying to get the desired result with OPENJSON() and CROSS APPLY, but I think that I'm not getting anywhere.
my original attempt was
DECLARE #json NVARCHAR(MAX) = N'[
{
"id":"1121131",
"idGroups":[
"123",
"X999"
],
"idGroupNames":[
"Neutral",
"Service"
]
},
{
"id":"2233154",
"idGroups":[
"654321"
],
"idGroupNames":[
"Position"
]
}
]'
SELECT id,idGroup,idGroupName FROM OPENJSON (#json)
WITH (
id INT 'strict $.id',
idGroups NVARCHAR(MAX) '$.idGroups' AS JSON,
idGroupNames NVARCHAR(MAX) '$.idGroupNames' AS JSON
) CROSS APPLY OPENJSON(idGroups)
WITH (
idGroup VARCHAR(500) '$'
) CROSS APPLY OPENJSON(idGroupNames)
WITH (
idGroupName VARCHAR(500) '$'
)
You need to use OPENJSON() with default schema and two additional APPLY operators. The following statement is a possible solution to your problem:
JSON:
DECLARE #json nvarchar(max) = N'[
{
"id":"1121131",
"idGroups":[
"123",
"X999"
],
"idGroupNames":[
"Neutral",
"Service"
]
},
{
"id":"2233154",
"idGroups":[
"654321"
],
"idGroupNames":[
"Position"
]
}
]'
Statement:
SELECT j.id, j1.[value] AS idGroups, j2.[value] AS idGroupNames
FROM OPENJSON(#json) WITH (
id nvarchar(7) '$.id',
idGroups nvarchar(max) '$.idGroups' AS JSON,
idGroupNames nvarchar(max) '$.idGroupNames' AS JSON
) j
CROSS APPLY OPENJSON(j.idGroups) j1
CROSS APPLY OPENJSON(j.idGroupNames) j2
WHERE j1.[key] = j2.[key]
You can do it without a third OPENJSON, using JSON_VALUE and a dynamic path.
This only works in SQL Server 2017+
DECLARE #json nvarchar(max) = N'[
{
"id":"1121131",
"idGroups":[
"123",
"X999"
],
"idGroupNames":[
"Neutral",
"Service"
]
},
{
"id":"2233154",
"idGroups":[
"654321"
],
"idGroupNames":[
"Position"
]
}
]';
SELECT j.id, j1.[value] AS idGroups, JSON_VALUE(j.idGroupNames, '$[' + j1.[key] + ']') AS idGroupNames
FROM OPENJSON(#json) WITH (
id nvarchar(7) '$.id',
idGroups nvarchar(max) '$.idGroups' AS JSON,
idGroupNames nvarchar(max) '$.idGroupNames' AS JSON
) j
CROSS APPLY OPENJSON(j.idGroups) j1;
db<>fiddle

How to use JSON_VALUE in where in clause (SQL Server)

: am trying to use part of a JSON to use as a filter for a where in clause as follows:
declare #json nvarchar(max) =
'[{
"brand_name": {
"key": "Brand Name",
"value": "''alpha'',''omega''"
}
}]';
select *
from someTable
where column in (select json_value(#json, '$[0].brand_name.value'));
How do I convert the value into a where-in argument?
Like this:
declare #json nvarchar(max) =
'[{
"brand_name": {
"key": "Brand Name",
"value": ["alpha","omega"]
}
}]';
select value from openjson(#json, '$[0].brand_name.value')

Remove array from json object stored in SQL Server column

If I have the following JSON stored in a column in SQL Server:
declare #json nvarchar(max) = '[{"id": "1", "name": "test1"},{"id": "2", "name": "test2"},{"id": "3", "name": "test4"}]'
how can I remove an array where Id:=1 ?
I want to get the following result:
[{"id": "2", "name": "test2"},{"id": "3", "name": "test4"}]
try this:
declare #json nvarchar(max) = '[{"id": "1", "name": "test1"},{"id": "2", "name": "test2"},{"id": "3", "name": "test4"}]';
DECLARE #IdTobeRemoved INT =1;
DECLARE #StartIndex INT
DECLARE #EndIndex INT
DECLARE #tempjson nvarchar(max)
SELECT #StartIndex = CHARINDEX('"id": "'+CONVERT(VARCHAR(10),#IdTobeRemoved)+'"',#json)-2
SELECT #tempjson = STUFF(#json, 1, #StartIndex, '')
SELECT #EndIndex =
CASE WHEN SUBSTRING(#tempjson,CHARINDEX('}',#tempjson)+1,1)=']'
THEN CHARINDEX('}',#tempjson)+1
ELSE CHARINDEX('}',#tempjson)+2
END
SELECT REPLACE(REPLACE(#json,SUBSTRING(#tempjson,0,#EndIndex),''),'},]','}]')