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

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'
)

Related

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

When insert JSON string to SQL table I only get the first record

I have the following json , when i am trying to extract it to sql i get only the first row can you please help.
DECLARE #json NVARCHAR(MAX);
SET #json = '{
"Reportno":{
"Env":{
"A":7140541,
"B":179001,
"C":"XML",
"D":"SLSPRV",
"F":90760,
"G":202104,
"H":2030,
"I":{
"J":20240118,
"K":202202014,
"L":22203,
"M":2020103,
"N":179001,
"O":9200005,
"P":{
"CustData":[
{
"CustCode":7295,
"Lines":{
"Barcode":13782,
"Qty":1,
"pack":"Box"
}
},
{
"CustCode":880,
"Lines":[
{
"Barcode":9245,
"Qty":2,
"pack":"unit"
},
{
"Barcode":5536,
"Qty":1,
"pack":"unit"
},
{
"Barcode":46199,
"Qty":3,
"pack":"Box"
},
{
"Barcode":738,
"Qty":1,
"pack":"unit"
},
{
"Barcode":149010,
"Qty":5,
"pack":"unit"
}
]
}
]
}
}
}
}
}'
SELECT *
FROM OPENJSON(#json, '$.Reportno.Env.I.P.CustData')
WITH (
CustCode varchar(150) '$.CustCode',
Barcode BIGINT '$.Lines.Barcode',
Qty SMALLINT '$.Lines.Qty',
pack NVARCHAR(max) '$.Lines.pack'
);
this is the resulte i get
this is what i need
Thank
You need to use 2 OPENJSON calls. The first for your CustData, adn then the 2nd for the nested JSON within Lines:
SELECT CD.CustCode,
L.Barcode,
L.Qty,
L.pack
FROM OPENJSON(#json, '$.Reportno.Env.I.P.CustData')
WITH (CustCode varchar(150) '$.CustCode',
Lines nvarchar(MAX) AS JSON) CD
CROSS APPLY OPENJSON(CD.Lines)
WITH(Barcode BIGINT '$.Barcode',
Qty SMALLINT '$.Qty',
pack NVARCHAR(max) '$.pack') L;

T-SQL Select from JSON returns JSON text is not properly formatted

Using a stored procedure in SQL Server 2016 (included below) I need to make a select from the nested JSON output from a remote file, but at my initial SELECT * I already have issues since the server returns that the JSON text is not properly formatted:
JSON text is not properly formatted. Unexpected character 'A' is found at position 0.
The T-SQL script I use:
CREATE PROCEDURE wget
#url VARCHAR(255),
#payload NVARCHAR(max) OUTPUT
AS
BEGIN
DECLARE #op TABLE (op nvarchar(max))
DECLARE #c varchar(1000) = N'powershell.exe -noprofile -executionpolicy bypass'
+ N'-command (Invoke-WebRequest -Uri "'+#url+'" -UseBasicParsing).content'
INSERT INTO #op
EXEC xp_cmdshell #c
SELECT #payload = STRING_AGG(op, '')
FROM #op
END
DECLARE #jsn nvarchar(max)
EXEC wget #url = 'https://<URL-WITH-JSON-DATA>',
#payload = #jsn output
SELECT *
FROM OPENJSON (#jsn, N'$')
2 array examples of the JSON file:
[
{
"Localnumber":"2931",
"Name":"Some Name",
"Description":null,
"Email":"mail#domain.dk",
"PhoneNumbers":[
{
"Number":"98765432",
"LineName":null,
"BelongsTo":"2931",
"GotoLocalNumber":null
},
{
"Number":"23456789",
"LineName":null,
"BelongsTo":"2931",
"GotoLocalNumber":null
},
{
"Number":"34567890",
"LineName":null,
"BelongsTo":"2931",
"GotoLocalNumber":null
}
],
"Phones":[
{
"LocalNumber":"200000",
"Name":null,
"Type":21,
"MAC":null,
"BelongsTo":"2931",
"description":"Myfone"
},
{
"LocalNumber":"200048",
"Name":null,
"Type":8,
"MAC":"20470396",
"BelongsTo":"2931",
"description":"Mobil (23456789)"
},
{
"LocalNumber":"200084",
"Name":null,
"Type":23,
"MAC":null,
"BelongsTo":"2931",
"description":"Microsoft Teams"
},
{
"LocalNumber":"200125",
"Name":null,
"Type":8,
"MAC":"23214202",
"BelongsTo":"2931",
"description":"Mobil (23456789)"
}
],
"Info":[
{
"Type":0,
"Label":null,
"Value":null
}
],
"Department":null
},
{
"Localnumber":"2931",
"Name":"Some Name",
"Description":null,
"Email":"mail#domain.dk",
"PhoneNumbers":[
{
"Number":"98765432",
"LineName":null,
"BelongsTo":"2931",
"GotoLocalNumber":null
},
{
"Number":"23456789",
"LineName":null,
"BelongsTo":"2931",
"GotoLocalNumber":null
},
{
"Number":"34567890",
"LineName":null,
"BelongsTo":"2931",
"GotoLocalNumber":null
}
],
"Phones":[
{
"LocalNumber":"200000",
"Name":null,
"Type":21,
"MAC":null,
"BelongsTo":"2931",
"description":"Myfone"
},
{
"LocalNumber":"200048",
"Name":null,
"Type":8,
"MAC":"20470396",
"BelongsTo":"2931",
"description":"Mobil (23456789)"
},
{
"LocalNumber":"200084",
"Name":null,
"Type":23,
"MAC":null,
"BelongsTo":"2931",
"description":"Microsoft Teams"
},
{
"LocalNumber":"200125",
"Name":null,
"Type":8,
"MAC":"23214202",
"BelongsTo":"2931",
"description":"Mobil (23456789)"
}
],
"Info":[
{
"Type":0,
"Label":null,
"Value":null
}
],
"Department":null
}
]
Can anyone point me in the right direction?
The solution was to import the JSON into a table with a varchar(MAX) from JScript server side:
Response.CacheControl = "no-cache"
Response.Expires = -1
Response.CodePage = 65001
Response.CharSet = "UTF-8"
var objSrvHTTP;
objSrvHTTP = Server.CreateObject ("Msxml2.ServerXMLHTTP.6.0");
objSrvHTTP.open ("GET","<%URL_TO_JSON_DATA%>", false);
objSrvHTTP.send ();
Response.ContentType = "application/json";
And then query the stored JSON from table using below query:
DECLARE #json NVARCHAR(MAX)
SET #json = (SELECT PHONE_DATA FROM EFP_JSON WHERE ID = 1)
SELECT PhoneNumber
FROM EFP_PhoneNumberSeries
WHERE REPLACE(PhoneNumber, ' ', '') NOT IN (
SELECT PhoneNumbers.Number
FROM OPENJSON(#json)
WITH (
Localnumber VARCHAR(50) '$.Localnumber',
Name VARCHAR(50) '$.Name',
Email VARCHAR(50) '$.Email',
PhoneNumbers nvarchar(max) '$.PhoneNumbers' AS JSON,
Phones nvarchar(max) '$.Phones' AS JSON
) as UserInfo
CROSS APPLY OPENJSON(PhoneNumbers)
WITH(
Number nvarchar(100) '$.Number',
LineName nvarchar(100) '$.LineName',
GotoLocalNumber nvarchar(100) '$.GotoLocalNumber',
BelongsTo nvarchar(100) '$.BelongsTo'
) as PhoneNumbers
CROSS APPLY OPENJSON(Phones)
WITH(
description nvarchar(100) '$.description'
) as description
)
AND REPLACE(PhoneNumber, ' ', '') NOT IN ('12345678','98765432')
AND (REPLACE(PhoneNumber, ' ', '') LIKE '1234%' OR REPLACE(PhoneNumber, ' ', '') LIKE '98765%');

Counting size of an array inside another JSON array: SQL SERVER

The JSON object structure is:
{
"store": {
"storeId":123,
"operations": {
"seasons": [
{
"storeHours": [{},{},{}] // consider it has 3 objects
},
{
"storeHours": [{},{}] //consider it has 2 objects
}
]
}
}
}
I want to count the size of "storeHours". I tried with:
DECLARE #count INT = 0;
DECLARE #destination NVARCHAR = (N'$.store.operations.seasons[' + #count + N'].storeHours');
Also tried with:
DECLARE #destination NVARCHAR = CONCAT(N'$.store.operations.seasons[', #count, N'].storeHours');
DECLARE #storeHoursCount INT = (
select count(A.[key]) as [count]
from (VALUES(#json)) V(J)
CROSS APPLY OPENJSON(V.J) WITH(
[storeHours] nvarchar(MAX) #destination AS JSON) S
CROSS APPLY OPENJSON(S.storeHours) A
);
I get an error:
Incorrect syntax near '#destination'
This works:
DECLARE #storeHoursCount INT = (
select count(A.[key]) as [count]
from (VALUES(#json)) V(J)
CROSS APPLY OPENJSON(V.J) WITH (
[storeHours] nvarchar(MAX) '$.store.operations.seasons[0].storeHours' AS JSON
) S
CROSS APPLY OPENJSON(S.storeHours) A
);
But I want it to be dynamic. Is there something that I am missing? Also what can be the reason for CONCAT() not working?
EDIT:
#Zhorov solution works really good when we want over all count of storeHours present. i.e Sum of all storeHours present in all seasons.
My requirement is to get count of storeHours based upon index season (eg: season[0]).
How can this be achieved?
Answer:
If I understand you correctly and you want to count all items in all nested $.storeHours arrays, the following approach is an option:
JSON:
DECLARE #count INT = 0;
DECLARE #destination nvarchar(max) = N'
{
"store": {
"storeId":123,
"operations": {
"seasons": [
{
"storeHours": []
}
]
}
}
}'
Statement:
SELECT #count = COUNT(*)
FROM OPENJSON(#destination, '$.store.operations.seasons') j1
CROSS APPLY OPENJSON(j1.[value], '$.storeHours') j2
SELECT #count
As an additional note, always specify the length of an nvarchar variable. When the length is not specified in a variable declaration statement, the default length is 1. In your case, the actual value of the #destination variable is simply $.
Update:
If you want to ...to get count of storeHours based upon index season ..., simply use the appropriate WHERE clause:
DECLARE #count INT = 0;
DECLARE #destination nvarchar(max) = N'
{
"store": {
"storeId":123,
"operations": {
"seasons": [
{
"storeHours": [{},{},{}]
},
{
"storeHours": [{},{}]
}
]
}
}
}'
SELECT #count = COUNT(*)
FROM OPENJSON(#destination, '$.store.operations.seasons') j1
CROSS APPLY OPENJSON(j1.[value], '$.storeHours') j2
WHERE CONVERT(int, j1.[key]) = 0

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