How to flatten nested array from JSON in SQL Server - sql

I am trying to flatten nested array from JSON in SQL Server, using the code below, but without success.
Used JSON
'{
"shipmentDetails": {
"shipmentId": "JHVJD5627278788"
},
"shipmentStops": [
{
"stopSequence": 1,
"orderReferenceNumbers": [
"2120549020", "test"
]
},
{
"stopSequence": 2,
"orderReferenceNumbers": [
"2120549020", "2120549002"
]
}
]
}'
DECLARE #Step AS NVARCHAR(max) = N'Variables declaration';
DECLARE #JSON1 AS NVARCHAR(MAX);
SET #JSON1 = '{
"shipmentDetails": {
"shipmentId": "JHVJD5627278788"
},
"shipmentStops": [
{
"stopSequence": 1,
"orderReferenceNumbers": [
"2120549020", "test"
]
},
{
"stopSequence": 2,
"orderReferenceNumbers": [
"2120549020", "2120549002"
]
}
]
}'
IF OBJECT_ID('JSONPO2') IS NOT NULL
DROP TABLE JSONPO2
SET #Step = N'JSON data parsing and loading into JSONPO2 temp table'
SELECT DISTINCT ShipDetails.shipmentId AS shipmentId
,ShipmentStops.stopSequence AS stopSequence
,ShipmentStops.orderReferenceNumbers AS orderReferenceNumbers
INTO JSONPO2
FROM OPENJSON(#JSON1) WITH (
shipmentDetails NVARCHAR(MAX) AS JSON
,shipmentStops NVARCHAR(MAX) AS JSON
) AS [Data]
CROSS APPLY OPENJSON(shipmentDetails) WITH (shipmentId NVARCHAR(20)) AS ShipDetails
CROSS APPLY OPENJSON(shipmentStops) WITH (
stopSequence INT
,orderReferenceNumbers NVARCHAR(MAX) AS JSON
) AS ShipmentStops
CROSS APPLY OPENJSON(orderReferenceNumbers) WITH (orderReferenceNumbers VARCHAR(max))
AS orderReferenceNumbers
SELECT *
FROM JSONPO2
From the above code, I receive only two rows with a strange array
shipmentId
stopSequence
orderReferenceNumbers
JHVJD5627278788
1
[ "2120549020", "test" ]
JHVJD5627278788
2
[ "2120549020", "2120549002" ]
How to change the code to parse nested arrays and have four rows like below?
shipmentId
stopSequence
orderReferenceNumbers
JHVJD5627278788
1
2120549020
JHVJD5627278788
1
test
JHVJD5627278788
2
2120549020
JHVJD5627278788
2
2120549002
Appreciate any help :)

Your issue is when trying to expand the array using the WITH clause:
CROSS APPLY OPENJSON(orderReferenceNumbers)
WITH (orderReferenceNumbers VARCHAR(max)) AS orderReferenceNumbers
The JSON you are opening though has no property "orderReferenceNumbers", it is simply:
["2120549020", "test"]
So this is returning NULL. You'd see this in your select if you were selecting orderReferenceNumbers.orderReferenceNumbers rather than ShipmentStops.orderReferenceNumbers.
You don't need to use WITH if you are opening a simple JSON array of primitive types. The following query will return the output you are after:
DECLARE #JSON1 AS NVARCHAR(MAX) = N'{
"shipmentDetails": {
"shipmentId": "JHVJD5627278788"
},
"shipmentStops": [
{
"stopSequence": 1,
"orderReferenceNumbers": [
"2120549020", "test"
]
},
{
"stopSequence": 2,
"orderReferenceNumbers": [
"2120549020", "2120549002"
]
}
]
}';
SELECT sd.shipmentId,
ss.stopSequence,
orderReferenceNumber = orn.Value
FROM OPENJSON(#JSON1)
WITH(shipmentDetails NVARCHAR(MAX) AS JSON, shipmentStops NVARCHAR(MAX) AS JSON) AS d
CROSS APPLY OPENJSON(d.shipmentDetails)
WITH(shipmentId NVARCHAR(20)) AS sd
CROSS APPLY OPENJSON(d.shipmentStops)
WITH(stopSequence INT, orderReferenceNumbers NVARCHAR(MAX) AS JSON) AS ss
CROSS APPLY OPENJSON(orderReferenceNumbers) AS orn;
Example on db<>fiddle
As an aside, if you will only ever have one shipmentId in the JSON (which would make sense otherwise you'd end up with cross joins) you can simplify this slightly, and remove one of the OPENJSON()s:
SELECT d.shipmentId,
ss.stopSequence,
orderReferenceNumber = orn.Value
FROM OPENJSON(#JSON1)
WITH(ShipmentId NVARCHAR(20) '$.shipmentDetails.shipmentId',
shipmentStops NVARCHAR(MAX) AS JSON) AS d
CROSS APPLY OPENJSON(d.shipmentStops)
WITH(stopSequence INT, orderReferenceNumbers NVARCHAR(MAX) AS JSON) AS ss
CROSS APPLY OPENJSON(ss.orderReferenceNumbers) AS orn;

Related

Return string in a array without object

This is how I am getting:
"skills":[
{
"cityname":"Kaduna North"
}
]
How I want is:
"skills":["Kaduna North"]
is that possible?
Try the below:
DECLARE #jsonInfo NVARCHAR(MAX)
SET #jsonInfo=N'{
"skills": [{
"cityname": "Kaduna North"
}]
}'
;
SELECT
concat('"skills":["',
JSON_VALUE(#jsonInfo,'$.skills[0].cityname')
,'"]'
)
;
Or use the below:
SELECT
JSON_QUERY(
concat('["',
JSON_VALUE(#jsonInfo,'$.skills[0].cityname')
,'"]'
) ) as skills
FOR JSON PATH

How to write a select query to get the index value from Json object

I have the below JSON object. I need to write a select query to get the index values of Object JSON array. Kind of getting the sequence value.
{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC",
},
{
"ID": 11,
"Name": "ABCD",
},
{
"ID": 15,
"Name": "ABCDE",
},
]
}]}
Expected Output:
Index_Value
1
2
3
If I understand the question correctly and you want to get the index of the items in the Object JSON array, you need to use OPENJSON() with default schema. The result is a table with columns key, value and type and in case of JSON array, the key column holds the index of each item in the array (0-based):
JSON:
DECLARE #json nvarchar(max) = N'{
"Model":[
{
"ModelName":"Test Model",
"Object":[
{
"ID":1,
"Name":"ABC"
},
{
"ID":11,
"Name":"ABCD"
},
{
"ID":15,
"Name":"ABCDE"
}
]
}
]
}'
Statement:
SELECT CONVERT(int, j2.[key]) + 1 AS item_id
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') j2
But if you want to get the values of the ID keys in the Object JSON array, the statement is different:
SELECT j2.ID
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') WITH (
ID int '$.ID'
) j2
Note, that you need two OPENJSON() calls, because the input JSON has nested array structure. Of course, if Model JSON array has always one item, you may simplify the statement using an appropriate path:
SELECT CONVERT(int, [key]) + 1 AS item_id
FROM OPENJSON (#json, '$.Model[0].Object')
Finally, to get index, ID and Name, you should use the following statement, which assumes, that $.Model JSON array has more than one item and defines ID and Name columns with the appropraite data types:
SELECT
CONVERT(int, j2.[key]) + 1 AS ItemID,
j3.ID, j3.Name
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value], '$.Object') j2
CROSS APPLY OPENJSON(j2.[value], '$') WITH (
ID int '$.ID',
Name varchar(50) '$.Name'
) j3
DECLARE #json nvarchar(max) = N'{
"Model":[
{
"ModelName":"Test Model",
"Object":[
{
"ID":1,
"Name":"ABC"
},
{
"ID":11,
"Name":"ABCD"
},
{
"ID":15,
"Name":"ABCDE"
}
]
}
]
}'
declare #i int=0;
SELECT
j2.ID, j2.Name
FROM OPENJSON (#json, '$.Model') j1
CROSS APPLY OPENJSON(j1.[value],concat('$.Object[',#i,']')) WITH (
ID i`enter code here`nt '$.ID', Name varchar(100) '$.Name'
) j2
Results:-
ID
Name
11
ABCD
you can select the key columns in select clause no need to mention in with of crossjoin.
SELECT
distinct t.id,
JSON_VALUE(AttsData.[value], '$.address') as address,
JSON_VALUE(AttsData.[value], '$.name') as name,
JSON_VALUE(AttsData.[value], '$.owner_name') as owner_name,
JSON_VALUE(AttsData.[value], '$.project') as project
,CONVERT(int, AttsData.[key]) index_id
FROM mytablewithjsonfeild t
CROSS APPLY OPENJSON (t."jsonfeild",N'$.parentkey') as AttsData
Above query, from the table I have cross joined the JSON field. and in select statement i have taken the specific keys.
and CONVERT(int, AttsData.[key]) to get the index of the elements

SQL Query on Json object inside multiple JSON objects

I have the below Json string. I need to write a query to get the TP records. Without providing the index value, we need get the result.
{
"S": [
{
"Name": "Project1",
"SP": [
{
"ID": 1,
"Name": "Test1",
"TP": [
{
"TID": 11,
"TName": "TT1",
},
{
"TID": 12,
"TName": "TT2",
},
]
},
{
"ID": 2,
"Name": "Test2",
"TP": [
{
"TID": 13,
"TName": "TT3",
},
{
"TID": 14,
"TName": "TT4",
},
]
},
]}]}
How do I query to get the TP values.
Expected Result:
TID TName
11 TT1
12 TT2
13 TT3
14 TT4
You can use OPENJSON function containing WITH Clause added as many CROSS APPLY Clause as upto see all sub-arrays :
SELECT S3.TID, S3.TName
FROM tab
CROSS APPLY OPENJSON(JsonData)
WITH ( S nvarchar(max) '$.S' AS JSON) AS S0
CROSS APPLY OPENJSON (S0.S)
WITH (
SP nvarchar(max) '$.SP' AS JSON ) S1
CROSS APPLY OPENJSON (S1.SP)
WITH (
TP nvarchar(max) '$.TP' AS JSON ) S2
CROSS APPLY OPENJSON (S2.TP)
WITH (
TID nvarchar(500) '$.TID',
TName nvarchar(500) '$.TName' ) S3
Demo
Update : If the first array S is fixed to contain only one item, we can reduce one-step as
SELECT S3.TID, S3.TName
FROM tab
CROSS APPLY OPENJSON(JsonData)
WITH (
SP nvarchar(max) '$.S[0].SP' AS JSON ) S1
CROSS APPLY OPENJSON (S1.SP)
WITH (
TP nvarchar(max) '$.TP' AS JSON ) S2
CROSS APPLY OPENJSON (S2.TP)
WITH (
TID nvarchar(500) '$.TID',
TName nvarchar(500) '$.TName' ) S3
Demo

JSON to SQL - Extracting from JSON Array?

I have an exercise to extract some data from a larger JSON object however the data is added as multiple objects or perhaps an array of sorts.
An example below;
DECLARE #json NVARCHAR(MAX) = '{
"N.data.-ce731645-e4ef-4784-bc02-bb90b4c9e9e6": "Some Data",
"N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f": [
{
"date_1": "2018-10-20T23:00:00.000Z"
},
{
"date_1": "2018-10-21T23:00:00.000Z"
}
]
}'
I need to extract these datetime entries from the "date_1" identifier into ideally a CSV list. From that I can do my own manipulations.
2018-10-20T23:00:00.000Z, 2018-10-21T23:00:00.000Z
I am familiar with JSON_VALUE() however not with its use outside of a simple piece of one dimensional data.
What I have so far;
DECLARE #json NVARCHAR(MAX) = '{
"N.data.-ce731645-e4ef-4784-bc02-bb90b4c9e9e6": "Some Data",
"N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f": [
{
"date_1": "2018-10-20T23:00:00.000Z"
},
{
"date_1": "2018-10-21T23:00:00.000Z"
}
]
}'
SELECT value FROM OPENJSON(#json)
Is there a way to achive the expected output outside of complex substring() and replace() uses?
Using SQL Server 2017
Microsoft SQL Server 2017 (RTM) - 14.0.1000.169 (X64) Aug 22 2017 17:04:49 Copyright (C) 2017 Microsoft Corporation Express Edition (64-bit) on Windows Server 2012 R2 Datacenter 6.3 <X64> (Build 9600: ) (Hypervisor)
Thanks
Since SQL Server 2017, the extraction can be done via native OPENJSON:
DECLARE #json NVARCHAR(MAX) = '{
"N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f": [
{
"date_1": "2018-10-20T23:00:00.000Z"
},
{
"date_1": "2018-10-21T23:00:00.000Z"
}
]
}'
SELECT
JSON_VALUE(child_value.value, '$.date_1') AS [key]
FROM OPENJSON(#json, '$') AS nda
cross apply openjson(nda.value, '$') as child_value
Results to:
key
2018-10-20T23:00:00.000Z
2018-10-21T23:00:00.000Z
Is there a way to adjust this to extract the values for a specific
key, "N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f" in the
example
In this case, that query can be slightly simplified to:
DECLARE #id nvarchar(200) = 'N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f'
SELECT
JSON_VALUE(nda.value, '$.date_1') AS [key]
FROM OPENJSON(#json, concat('$."',#id,'"')) AS nda
or without parametrization:
SELECT
JSON_VALUE(nda.value, '$.date_1') AS [key]
FROM OPENJSON(#json, '$."N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f"') AS nda
Use a cross apply with OPENJSON() using a with_clause:
DECLARE #json NVARCHAR(MAX) = '{
"N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f": [
{
"date_1": "2018-10-20T23:00:00.000Z"
},
{
"date_1": "2018-10-21T23:00:00.000Z"
}
]
}';
SELECT [b].*
FROM OPENJSON(#json) [a]
CROSS APPLY
OPENJSON([a].[Value])
WITH (
[date_1] DATETIME '$.date_1'
) [b];
Another possible approach, using OPENJSON(). With this approach you can get key/value pairs from your nested JSON array, even if this array has different key names.
DECLARE #json nvarchar(max)
SET #json =
N'{"N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f": [
{
"date_1": "2018-10-20T23:00:00.000Z"
},
{
"date_1": "2018-10-21T23:00:00.000Z"
},
{
"date_2": "2019-10-21T23:00:00.000Z"
}
]
}'
SELECT
x.[key] AS SessionData,
z.[key],
z.[value]
FROM OPENJSON(#json) x
CROSS APPLY (SELECT * FROM OPENJSON(x.[value])) y
CROSS APPLY (SELECT * FROM OPENJSON(y.[value])) z
--WHERE z.[key] = 'date_1'
Output:
SessionData key value
N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f date_1 2018-10-20T23:00:00.000Z
N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f date_1 2018-10-21T23:00:00.000Z
N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f date_2 2019-10-21T23:00:00.000Z
Update:
If you want to filter by key name, next may help:
DECLARE #json NVARCHAR(MAX) = '{
"N.data.-ce731645-e4ef-4784-bc02-bb90b4c9e9e6": "Some Data",
"N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f": [
{
"date_1": "2018-10-20T23:00:00.000Z"
},
{
"date_1": "2018-10-21T23:00:00.000Z"
}
]
}'
SELECT z.[value]
--SELECT STRING_AGG(z.[value], ', ') [Data] -- with string aggregation
FROM OPENJSON(#json) x
CROSS APPLY (SELECT * FROM OPENJSON(x.[value])) y
CROSS APPLY (SELECT * FROM OPENJSON(y.[value])) z
WHERE x.[key] = 'N.data.sessionDates-7f1790d3-9175-43aa-962b-161ee3b8615f'
Output:
value
2018-10-20T23:00:00.000Z
2018-10-21T23:00:00.000Z
-- With string aggregation
--Data
--2018-10-20T23:00:00.000Z, 2018-10-21T23:00:00.000Z

Concatenate nested JSON in SQL Server

I am trying to concatenate nested JSON in SQL, how to do that.
Please help me out with this.
Example: I have JSON Like below.
[
{
"Name": "Cards",
"Value": [
"Pack of 24 Dare Cards"
],
"SourceSpecified": false,
"Any": []
},
{
"Name": "Boppers and Shot Glasses",
"Value": [
"12 Willy Shot Glass and 12 Hen Boppers"
],
"SourceSpecified": false,
"Any": []
}
]
I want Out Put:
Pack of 24 Dare Cards-12 Willy Shot Glass and 12 Hen Boppers
If Value always contains one element
DECLARE #JSON NVARCHAR(MAX) = ' [
{
"Name": "Cards",
"Value": [
"Pack of 24 Dare Cards"
],
"SourceSpecified": false,
"Any": []
},
{
"Name": "Boppers and Shot Glasses",
"Value": [
"12 Willy Shot Glass and 12 Hen Boppers"
],
"SourceSpecified": false,
"Any": []
}
]'
SELECT STRING_AGG(Val, '-')
FROM OPENJSON(#JSON)
WITH (Val NVARCHAR(MAX) '$.Value[0]')
Support of multiple values is also easy.
SELECT STRING_AGG(Val, '-')
FROM OPENJSON(#JSON)
WITH (VALUE NVARCHAR(MAX) '$.Value' AS JSON)
OUTER APPLY OPENJSON(VALUE) WITH (Val NVARCHAR(MAX) '$')
This one I achieved with creating a scalar-valued function to populate concatenated value.
Create FUNCTION [dbo].[test]
(
#VariationData nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
Declare #val nvarchar(max)
select #val = COALESCE(#val+', ','')+ Value from OPENJSON(#VariationData)
With ([Value] NVARCHAR(MAX) N'$.Value[0]')
return #val
END
Call function with nested JSON value i.e
Select [dbo].[test] (#JSON);
This worked for me.