MS SQL Json array processing with separate arrays - json

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

Related

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

SQL Parse Json array to rows

I'm trying to parse the 'custinfo' array to rows, rather than specific columns how I have in my query (there can be none or many values in the array)
DECLARE #json NVARCHAR(MAX) ='{
"customer": [
{
"id": "123",
"history": [
{
"id": "a123",
"dates": [
{
"date": "2022-03-19",
"details": {
"custinfo": [
"male",
"married"
],
"age": 40
}}]}]}]}'
SELECT
JSON_VALUE ( j.[value], '$.id' ) AS CustId,
JSON_VALUE ( m.[value], '$.id' ) AS CustId_Hist,
JSON_VALUE ( a1.[value], '$.date' ) AS date,
JSON_VALUE ( a1.[value], '$.details.age' ) AS age,
JSON_VALUE ( a1.[value], '$.details.custinfo[0]' ) AS custinfo0,
JSON_VALUE ( a1.[value], '$.details.custinfo[1]' ) AS custinfo1
FROM OPENJSON( #json, '$."customer"' ) j
CROSS APPLY OPENJSON ( j.[value], '$."history"' ) AS m
CROSS APPLY OPENJSON ( m.[value], '$."dates"' ) AS a1
Desired results:
Like I mentioned in the comments, I would switch to using WITH clauses and defining your columns and their data types. You can then also get the values, into 2 separate rows you want, with the following. Note tbhe extra OPENJSON at the end, which treats the custinfo as the array it is; returning 2 rows (1 for each value in the array):
DECLARE #json NVARCHAR(MAX) ='{
"customer": [
{
"id": "123",
"history": [
{
"id": "a123",
"dates": [
{
"date": "2022-03-19",
"details": {
"custinfo": [
"male",
"married"
],
"age": 40
}}]}]}]}';
SELECT c.id AS CustId,
h.id AS CustId_Hist,
d.date AS date,
d.age AS age,
ci.[value] AS custinfo
FROM OPENJSON( #json,'$.customer')
WITH (id int,
history nvarchar(MAX) AS JSON) c
CROSS APPLY OPENJSON (c.history)
WITH (id varchar(10),
dates nvarchar(MAX) AS JSON) h
CROSS APPLY OPENJSON (h.dates)
WITH(date date,
details nvarchar(MAX) AS JSON,
age int '$.details.age') d
CROSS APPLY OPENJSON(d.details,'$.custinfo') ci;

Remove elements from a JSON array, with no key

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
}
]

How do I use OPENJSON on the result of a sub OPENJSON

I have a JSON structure like this:
Declare #layout NVARCHAR(MAX) = N'
{
"Sections": [
{
"SectionName":"Section1",
"SectionOrder":1,
"Fields":[
{
"FieldName":"Field1",
"FieldData":"Data1"
},
{
"FieldName":"Field2",
"FieldData":"Data2"
}
]
},
{
"SectionName":"Section2",
"SectionOrder":2,
"Fields":[
{
"FieldName":"Field3",
"FieldData":"Data3"
},
{
"FieldName":"Field4",
"FieldData":"Data4"
}
]
}
]
}
'
How do I query to the equivalent of Sections.Fields.FieldName = 'Field3'?
select *
from OPENJSON(#layout,'$.Sections')
WITH (
SectionName nvarchar(MAX) '$.SectionName',
SectionOrder nvarchar(MAX) '$.SectionOrder',
Fields nvarchar(MAX) '$.Fields' as JSON
)
This is as far as I got, I can't go down any further or it will not return any results.
Figured it out after reading documentation, fully works!
SELECT SectionName, FieldName, FieldData FROM (
select *
from OPENJSON(#layout,'$.Sections')
WITH (
SectionName nvarchar(MAX) '$.SectionName',
SectionOrder nvarchar(MAX) '$.SectionOrder',
Fields nvarchar(MAX) '$.Fields' as JSON
)
) as Sections
CROSS APPLY OPENJSON(Fields,'$')
WITH (
FieldName nvarchar(MAX) '$.FieldName',
FieldData nvarchar(MAX) '$.FieldData'
)

How do you OPENJSON on Arrays of Arrays

I have a JSON structure where there are Sections, consisting of multiple Renders, which consist of multiple Fields.
How do I do 1 OPENJSON call on the lowest level (Fields) to get all information from there?
Here is an example JSON:
Declare #layout NVARCHAR(MAX) = N'
{
"Sections": [
{
"SectionName":"Section1",
"SectionOrder":1,
"Renders":[
{
"RenderName":"Render1",
"RenderOrder":1,
"Fields":[
{
"FieldName":"Field1",
"FieldData":"Data1"
},
{
"FieldName":"Field2",
"FieldData":"Data2"
}
]
},
{
"RenderName":"Render2",
"RenderOrder":2,
"Fields":[
{
"FieldName":"Field1",
"FieldData":"Data1"
},
{
"FieldName":"Field2",
"FieldData":"Data2"
}
]
}
]
},
{
"SectionName":"Section2",
"SectionOrder":2,
"Renders":[
{
"RenderName":"Render1",
"RenderOrder":1,
"Fields":[
{
"FieldName":"Field1",
"FieldData":"Data1"
}
]
},
{
"RenderName":"Render2",
"RenderOrder":2,
"Fields":[
{
"FieldName":"Field1",
"FieldData":"Data1"
},
{
"FieldName":"Field2",
"FieldData":"Data2"
}
]
}
]
}
]
}
'
Here is some example of code of a nested OPENJSON call, which works, but is very complex and can't be generated dynamically, how do I make it one level call?
SELECT SectionName, SectionOrder, RenderName, RenderOrder, FieldName, FieldData FROM (
SELECT SectionName, SectionOrder, RenderName, RenderOrder, Fields FROM (
select SectionName, SectionOrder, Renders
from OPENJSON(#layout,'$.Sections')
WITH (
SectionName nvarchar(MAX) '$.SectionName',
SectionOrder nvarchar(MAX) '$.SectionOrder',
Renders nvarchar(MAX) '$.Renders' as JSON
)
) as Sections
CROSS APPLY OPENJSON(Renders,'$')
WITH (
RenderName nvarchar(MAX) '$.RenderName',
RenderOrder nvarchar(MAX) '$.RenderOrder',
Fields nvarchar(MAX) '$.Fields' as JSON
)
) as Renders
CROSS APPLY OPENJSON(Fields,'$')
WITH (
FieldName nvarchar(MAX) '$.FieldName',
FieldData nvarchar(MAX) '$.FieldData'
)
This is what I would like to achieve:
select FieldName, FieldData
from OPENJSON(#layout,'$.Sections.Renders.Fields')
WITH (
FieldName nvarchar(MAX) '$.Sections.Renders.Fields.FieldName',
FieldData nvarchar(MAX) '$.Sections.Renders.Fields.FieldData'
)
While you can't get away with using only a single OPENJSON, you can simplify your query a bit to make it easier to create dynamically by removing the nested subqueries:
SELECT SectionName, SectionOrder, RenderName, RenderOrder, FieldName, FieldData
FROM OPENJSON(#layout, '$.Sections')
WITH (
SectionName NVARCHAR(MAX) '$.SectionName',
SectionOrder NVARCHAR(MAX) '$.SectionOrder',
Renders NVARCHAR(MAX) '$.Renders' AS JSON
)
CROSS APPLY OPENJSON(Renders,'$')
WITH (
RenderName NVARCHAR(MAX) '$.RenderName',
RenderOrder NVARCHAR(MAX) '$.RenderOrder',
Fields NVARCHAR(MAX) '$.Fields' AS JSON
)
CROSS APPLY OPENJSON(Fields,'$')
WITH (
FieldName NVARCHAR(MAX) '$.FieldName',
FieldData NVARCHAR(MAX) '$.FieldData'
)
EDIT:
If you have a primitive array, you can access the data using the value property after you expose the nested array as a JSON field. Using the JSON from the comment below, you can do this to get the values from a primitive array:
DECLARE #layout NVARCHAR(MAX) = N'{ "id":123, "locales":["en", "no", "se"] }'
SELECT
a.id
, [Locale] = b.value
FROM OPENJSON(#layout, '$')
WITH (
id INT '$.id',
locales NVARCHAR(MAX) '$.locales' AS JSON
) a
CROSS APPLY OPENJSON(a.locales,'$') b
This can be done by CROSS Applying the JSON child node with the parent node and using the JSON_Value() function, like shown below:
DECLARE #json NVARCHAR(1000)
SELECT #json =
N'{
"OrderHeader": [
{
"OrderID": 100,
"CustomerID": 2000,
"OrderDetail": [
{
"ProductID": 2000,
"UnitPrice": 350
},
{
"ProductID": 3000,
"UnitPrice": 450
},
{
"ProductID": 4000,
"UnitPrice": 550
}
]
}
]
}'
SELECT
JSON_Value (c.value, '$.OrderID') as OrderID,
JSON_Value (c.value, '$.CustomerID') as CustomerID,
JSON_Value (p.value, '$.ProductID') as ProductID,
JSON_Value (p.value, '$.UnitPrice') as UnitPrice
FROM OPENJSON (#json, '$.OrderHeader') as c
CROSS APPLY OPENJSON (c.value, '$.OrderDetail') as p
Result
-------
OrderID CustomerID ProductID UnitPrice
100 2000 2000 350
100 2000 3000 450
100 2000 4000 550
select
json_query(questionsList.value, '$.answers'),
json_value(answersList2.value, '$.text[0].text') as text,
json_value(answersList2.value, '$.value') as code
from RiskAnalysisConfig c
outer apply OpenJson(c.Configuration, '$.questions') as questionsList
outer apply OpenJson(questionsList.value, '$.answers') as answersList2
where questionsList.value like '%"CATEGORIES"%'
Sample of List of answers for each question into list
questionsList list level 1
answersList2 list level 2
Json sample
{
"code": "Code x",
"questions": [
{},
{},
{
"code": "CATEGORIES",
"text": [
{
"text": "How old years are you?",
"available": true
}
],
"answers": [
{
"text": [
{
"text": "more than 18",
"available": true
}
],
"text": [
{
"text": "less than 18",
"available": true
}
]
}
]
}
]
}
Partial result
| text | code |
| --- | --- |
| How old years are you? | more than 18 |
| How old years are you? | lass than 18 |
I have the JSON code and inserted into the table called MstJson, the column name which contains JSON code is JSON data.
JSON Code :
[
{
"id":100,
"type":"donut",
"name":"Cake",
"ppu":0.55,
"batters":{
"batter":[
{
"id":"1001",
"type":"Regular"
},
{
"id":"1002",
"type":"Chocolate"
},
{
"id":"1003",
"type":"Blueberry"
},
{
"id":"1004",
"type":"Havmor",
"BusinessName":"HussainM"
},
"id",
"type"
]
},
"topping":[
{
"id":"5001",
"type":"None"
},
{
"id":"5002",
"type":"Glazed"
},
{
"id":"5005",
"type":"Sugar"
},
{
"id":"5007",
"type":"Powdered Sugar"
},
{
"id":"5006",
"type":"Chocolate with Sprinkles"
},
{
"id":"5003",
"type":"Chocolate"
},
{
"id":"5004",
"type":"Maple"
}
]
},
{
"id":"0002",
"type":"donut",
"name":"Raised",
"ppu":0.55,
"batters":{
"batter":[
{
"id":"1001",
"type":"Regular"
}
]
},
"topping":[
{
"id":"5001",
"type":"None"
},
{
"id":"5002",
"type":"Glazed"
},
{
"id":"5005",
"type":"Sugar"
},
{
"id":"5003",
"type":"Chocolate"
},
{
"id":"5004",
"type":"Maple"
}
]
},
{
"id":"0003",
"type":"donut",
"name":"Old Fashioned",
"ppu":0.55,
"batters":{
"batter":[
{
"id":"1001",
"type":"Regular"
},
{
"id":"1002",
"type":"Chocolate"
}
]
},
"topping":[
{
"id":"5001",
"type":"None"
},
{
"id":"5002",
"type":"Glazed"
},
{
"id":"5003",
"type":"Chocolate"
},
{
"id":"5004",
"type":"Maple"
}
]
}
]
Sql Code for OpenJson using CrossApply (Nested Array):
SELECT
d.ID
,a.ID
,a.Type
,a.Name
,a.PPU
,c.Batterid
,c.Battertype
FROM MstJson d
CROSS APPLY
OPENJSON(Jsondata)
WITH
(
ID NVARCHAR(MAX) '$.id'
,Type NVARCHAR(MAX) '$.type'
,Name NVARCHAR(MAX) '$.name'
,PPU DECIMAL(18, 2) '$.ppu'
,Batters NVARCHAR(MAX) '$.batters' AS JSON
) AS a
CROSS APPLY
OPENJSON(Batters, '$')
WITH
(
Batter NVARCHAR(MAX) '$.batter' AS JSON
) AS b
CROSS APPLY
OPENJSON(Batter, '$')
WITH
(
Batterid INT '$.id'
,Battertype NVARCHAR(MAX) '$.type'
) AS c
WHERE d.ID = 12; ---above Json Code is on Id 12 of Table MstJson