How do you OPENJSON on Arrays of Arrays - json

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

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

Querying nested Json with SQL Server

I have this content in a table in a column JsonResponse:
{
"results": {
"meta": {
"number_of_node": 2
},
"data": {
"Node": [
{
"id": "44511",
"subject": 31366
},
{
"id": "72176",
"subject": 36508
}
],
}
}
}
I'm trying to extract ALL the "subject"; here my query:
SELECT
JSON_VALUE(JSonResponse, '$.results.data.Node.subject') AS JsonResponse
FROM
Table
but the query result is always null.
I didn't find documentation about querying nested Json, any tips?
Thanks!
Try this:
DECLARE #Table TABLE (
JSonResponse NVARCHAR(MAX)
);
INSERT #Table ( JSonResponse )
VALUES (
'{
"results": {
"meta": {
"number_of_node": 2
},
"data": {
"Node": [
{
"id": "44511",
"subject": 31366
},
{
"id": "72176",
"subject": 36508
}
]
}
}
}'
);
SELECT oj2.*
FROM #Table AS t
CROSS APPLY
OPENJSON(t.JSonResponse, '$.results')
WITH ( meta NVARCHAR(MAX) AS JSON, data NVARCHAR(MAX) AS JSON ) AS oj
CROSS APPLY
OPENJSON(oj.data, '$.Node')
WITH ( id INT, subject NVARCHAR(MAX)) AS oj2;
You don't necessarily need nested OPENJSON calls, as you can jump straight to the right path
SELECT oj.*
FROM #Table AS t
CROSS APPLY OPENJSON(t.JSonResponse, '$.results.data.Node')
WITH (
id int,
subject int
) AS oj;
db<>fiddle

How to join nested JSON indices to multiple rows in SQL by primary key

I am trying to update several rows in SQL with JSON.
I'd like to match a primary key on a table row to an index nested in an array of JS objects.
Sample data:
let json = [{
"header": object_data,
"items": [{
"id": {
"i": 0,
"name": "item_id"
},
"meta": {
"data": object_data,
"text": "some_text"
}
}, {
"id": {
"i": 4,
"name": "item_id4"
},
"meta": {
"data": object_data,
"text": "some_text"
}
}, {
"id": {
"i": 17,
"name": "item_id17"
},
"meta": {
"data": object_data,
"text": "some_text"
}}]
}]
Sample table:
i | json | item_id
---+---------------------------+---------
0 | entire_object_at_index_0 | item_id
4 | entire_object_at_index_4 | item_id4
17 | entire_object_at_index_17 | item_id17
entire_object_at_index, meaning appending the item data to the header to create a new object for each row.
"header" "some_data",
"items": [{
"id": {
"i": 0,
"name": "item_id1"
},
"meta": {
"data": "some_data",
"text": "some_text"
}
}]
SQL:
update someTable set
json = json_value(#jsons, '$') -- not sure how to join on index here
item_id = json_value(#jsons, '$.items[?].id.name' -- not sure how to select by index here
where [i] = json_query(#jsons, '$.items.id.i')
The requirement to repeat the other properties complicates this a bit, because we need to build a new object explicitly. Even so it's not too hard:
update someTable
set
[json] = (
select (
select
"header" = json_query(#json, '$.header'),
"items" = json_query(N'[' + items.item + N']')
for json path, without_array_wrapper
)
),
item_id = items.item_id
from openjson(#json, '$.items') with (
item nvarchar(max) '$' as json,
item_id varchar(50) '$.id.name',
i int '$.id.i'
) items
join someTable on [someTable].i = items.i
Here I'm assuming the #json has already been unwrapped from its array, as your query seems to assume. If it's not, substitute $.[0] for $ in the outer query.
Update:
It's an attempt to improve my answer (I missed the header part of the JSON content in the original answer). Of course, the #JeroenMostert's answer is an excellent solution, so this is just another possible approach. Note, that if header part of JSON content is scalar value, you should use JSON_VALUE().
Table and JSON:
-- Table
CREATE TABLE #Data (
i int,
[json] nvarchar(max),
item_id nvarchar(100)
)
INSERT INTO #Data
(i, [json], [item_id])
VALUES
(0 , N'entire_object_at_index_0', N'item_id'),
(4 , N'entire_object_at_index_4', N'item_id4'),
(17, N'entire_object_at_index_17', N'item_id17')
-- JSON
DECLARE #json nvarchar(max) = N'[{
"header": {"key": "some_data"},
"items": [{
"id": {
"i": 0,
"name": "item_id"
},
"meta": {
"data": "some_data",
"text": "some_text"
}
}, {
"id": {
"i": 4,
"name": "item_id4"
},
"meta": {
"data": "some_data",
"text": "some_text"
}
}, {
"id": {
"i": 17,
"name": "item_id17"
},
"meta": {
"data": "some_data",
"text": "some_text"
}}]
}]'
Statement:
UPDATE #Data
SET #Data.Json = j.Json
FROM #Data
CROSS APPLY (
SELECT
JSON_QUERY(#json, '$[0].header') AS header,
JSON_QUERY(j.[value], '$') AS items
FROM OPENJSON(#json, '$[0].items') j
WHERE JSON_VALUE(j.[value], '$.id.i') = #Data.[i]
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) j ([Json])
Original answer:
One possible approach is to use OPENJSON and appropriate join:
Table and JSON:
-- Table
CREATE TABLE #Data (
i int,
[json] nvarchar(max),
item_id nvarchar(100)
)
INSERT INTO #Data
(i, [json], [item_id])
VALUES
(0 , N'entire_object_at_index_0', N'item_id'),
(4 , N'entire_object_at_index_4', N'item_id4'),
(17, N'entire_object_at_index_17', N'item_id17')
-- JSON
DECLARE #json nvarchar(max) = N'[{
"header": "some_data",
"items": [{
"id": {
"i": 0,
"name": "item_id"
},
"meta": {
"data": "some_data",
"text": "some_text"
}
}, {
"id": {
"i": 4,
"name": "item_id4"
},
"meta": {
"data": "some_data",
"text": "some_text"
}
}, {
"id": {
"i": 17,
"name": "item_id17"
},
"meta": {
"data": "some_data",
"text": "some_text"
}}]
}]'
Statement:
UPDATE #Data
SET [json] = j.[value]
FROM #Data
LEFT JOIN (
SELECT
[value],
JSON_VALUE([value], '$.id.i') AS [i]
FROM OPENJSON(#json, '$[0].items')
) j ON (#Data.[i] = j.[i])

Convert table result to a form of JSON

I am trying to form json from a table result set :
create table testmalc(
appid int identity(1,1),
propertyid1 int ,
propertyid1val varchar(10) ,
propertyid2 int,
propertyid2val varchar(10) ,
)
insert into testmalc values(456,'t1',789,'t2')
insert into testmalc values(900,'t3',902,'t4')
need below desired JSON result :
{
"data": {
"record": [{
"id": appid,
"customFields": [{
"customfieldid": propertyid1 ,
"customfieldvalue": propertyid1val
},
{
"customfieldid": propertyid2 ,
"customfieldvalue": propertyid2val
}
]
},
{
"id": appid,
"customFields": [{
"customfieldid": propertyid1 ,
"customfieldvalue": propertyid1val
},
{
"customfieldid": propertyid2 ,
"customfieldvalue": propertyid2val
}
]
}
]
}
}
I am trying to use stuff but was not getting the desired result. Now trying with UnPivot.
If you cannot upgrade to SQL-Server 2016 for JSON support you should try to solve this in any application / programming language you know of.
Just for fun, I provide an approach, which works, but is more a hack than a solution:
Your test data:
DECLARE #testmalc table (
appid int identity(1,1),
propertyid1 int ,
propertyid1val varchar(10) ,
propertyid2 int,
propertyid2val varchar(10)
);
insert into #testmalc values(456,'t1',789,'t2')
,(900,'t3',902,'t4');
--create a XML, which is the most similar structure and read it as a NVARCHAR string
DECLARE #intermediateXML NVARCHAR(MAX)=
(
SELECT t.appid AS id
,(
SELECT t2.propertyid1 AS [prop1/#customfieldid]
,t2.propertyid1val AS [prop1/#customfieldvalue]
,t2.propertyid2 AS [prop2/#customfieldid]
,t2.propertyid2val AS [prop2/#customfieldvalue]
FROM #testmalc t2
WHERE t2.appid=t.appid
FOR XML PATH('customFields'),TYPE
) AS [*]
FROM #testmalc t
GROUP BY t.appid
FOR XML PATH('row')
);
--Now a bunch of replacements
SET #intermediateXML=REPLACE(REPLACE(REPLACE(REPLACE(#intermediateXML,'=',':'),'/>','}'),'<prop1 ','{'),'<prop2 ','{');
SET #intermediateXML=REPLACE(REPLACE(REPLACE(REPLACE(#intermediateXML,'<customFields>','"customFields":['),'</customFields>',']'),'customfieldid','"customfieldid"'),'customfieldvalue',',"customfieldvalue"');
SET #intermediateXML=REPLACE(REPLACE(#intermediateXML,'<id>','"id":'),'</id>',',');
SET #intermediateXML=REPLACE(REPLACE(REPLACE(#intermediateXML,'<row>','{'),'</row>','}'),'}{','},{');
DECLARE #json NVARCHAR(MAX)=N'{"data":{"record":[' + #intermediateXML + ']}}';
PRINT #json;
The result (formatted)
{
"data": {
"record": [
{
"id": 1,
"customFields": [
{
"customfieldid": "456",
"customfieldvalue": "t1"
},
{
"customfieldid": "789",
"customfieldvalue": "t2"
}
]
},
{
"id": 2,
"customFields": [
{
"customfieldid": "900",
"customfieldvalue": "t3"
},
{
"customfieldid": "902",
"customfieldvalue": "t4"
}
]
}
]
}
}

How to call Json in sql query

I want to display the parameter ST and NextTime from table #json. The parameters id and Timestamp appear normally. I try the following but does not show any effect.Any possible answers?
My Json
{
"PCol": [{
"Id": 15,
"TimeStamp": "2018-02-1",
"Val": {
"States": [{
"Numbers": {
"Number": [5, 8]
},
"CS": {
"ST": "25"
},
"Changes": [{
"NextTime": 1
}]
}]
}
}]
}
My Sql Query
SELECT * FROM OPENJSON((select * from #json),N'$.PCol')
WITH (
[Id] INT N'$.Id ',
[TimeStamp] NVARCHAR(MAX) N'$.TimeStamp',
**[ST] INT N'$.Val.States.CS.ST'**
)
Thanks
Not going to lie, my OPENJSON knowledge is poor (so this might be able to be more succinct), however, this works:
DECLARE #JSON nvarchar(MAX) =
N' {
"PCol": [{
"Id": 15,
"TimeStamp": "2018-02-1",
"Val": {
"States": [{
"Numbers": {
"Number": [5, 8]
},
"CS": {
"ST": "25"
}
}]
}
}]
} ';
SELECT P.Id,
P.TimeStamp,
V.ST
FROM OPENJSON(#JSON,N'$.PCol') WITH ([Id] INT N'$.Id ',
[TimeStamp] NVARCHAR(MAX) N'$.TimeStamp',
[Vals] nvarchar(MAX) N'$.Val' AS JSON) P
CROSS APPLY OPENJSON(P.Vals,N'$.States') WITH (ST int N'$.CS.ST') V
For the latest JSON, this works:
SELECT P.Id,
P.TimeStamp,
V.ST,
C.NextTime
FROM OPENJSON(#JSON,N'$.PCol') WITH ([Id] INT N'$.Id ',
[TimeStamp] NVARCHAR(MAX) N'$.TimeStamp',
[Vals] nvarchar(MAX) N'$.Val' AS JSON) P
CROSS APPLY OPENJSON(P.Vals,N'$.States') WITH (ST int N'$.CS.ST',
[Changes] nvarchar(MAX) N'$.Changes' AS JSON) V
CROSS APPLY OPENJSON(V.[Changes]) WITH (NextTime int '$.NextTime') C