JSON to SQL - Extracting from JSON Array? - sql

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

Related

SQL OPENJSON get only first element

I'm trying to get data from only the first element in the JSON string because the first element will always be the newest. I have tried a few things with no luck. Maybe someone here can point me in the right direction.
This is the JSON string:
[
{
"wd:Transaction_Log_Entry": [
{
"wd:Transaction_Log_Reference": {
"#wd:Descriptor": "Rescind of End Contract: XXXX (Rescinded)",
"wd:ID": {
"#wd:type": "WID",
"#text": "fb27bafef89b101b5bf865947b420000"
}
},
"wd:Transaction_Log_Data": {
"wd:Transaction_Log_Description": "Rescind of End Contract: XXXX (Rescinded)",
"wd:Transaction_Effective_Moment": "2023-01-18T09:00:00+01:00",
"wd:Transaction_Entry_Moment": "2023-01-19T10:49:00.868+01:00",
"wd:Is_Rescind_Or_Rescinded": "1",
"wd:Is_Correction_Or_Corrected": "0"
}
},
{
"wd:Transaction_Log_Reference": {
"#wd:Descriptor": "End Contract: XXXX (Rescinded)",
"wd:ID": {
"#wd:type": "WID",
"#text": "a4a0fd2c2df8101bd1c6bde5f5710000"
}
},
"wd:Transaction_Log_Data": {
"wd:Transaction_Log_Description": "End Contract: XXXX (Rescinded)",
"wd:Transaction_Effective_Moment": "2023-01-18T09:00:00+01:00",
"wd:Transaction_Entry_Moment": "2023-01-18T12:41:43.867+01:00",
"wd:Is_Rescind_Or_Rescinded": "1",
"wd:Is_Correction_Or_Corrected": "0"
}
}
]
}
]
And my SQL query - I'm selecting the JSON string from a table that has the column name 'Transaction_Log_Entry_Data'
SELECT Effective_Moment, Entry_Moment
FROM [dbo].[Daily_And_Future_Terminations_Transaction_Log_Source]
CROSS APPLY OPENJSON (Transaction_Log_Entry_Data, '$[0]."wd:Transaction_Log_Entry"')
WITH (
Effective_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Effective_Moment"',
Entry_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Entry_Moment"'
)
And my results is 2 rows and I only want data from the first element:
Best regards
Ole
You have an arry in an array, so you must write.
JSOn is an interesting data structure, but you need a lot of experience with it
SELECT Effective_Moment, Entry_Moment
FROM [Daily_And_Future_Terminations_Transaction_Log_Source]
CROSS APPLY OPENJSON (Transaction_Log_Entry_Data, '$[0]."wd:Transaction_Log_Entry"[0]')
WITH (
Effective_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Effective_Moment"',
Entry_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Entry_Moment"'
)
Effective_Moment
Entry_Moment
2023-01-18T09:00:00+01:00
2023-01-19T10:49:00.868+01:00
fiddle
SELECT TOP 1 Effective_Moment, Entry_Moment
FROM [Daily_And_Future_Terminations_Transaction_Log_Source]
CROSS APPLY OPENJSON (Transaction_Log_Entry_Data, '$[0]."wd:Transaction_Log_Entry"')
WITH (
Effective_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Effective_Moment"',
Entry_Moment NVARCHAR(50) '$."wd:Transaction_Log_Data"."wd:Transaction_Entry_Moment"'
)
ORDEr By Entry_Moment DESC
Effective_Moment
Entry_Moment
2023-01-18T09:00:00+01:00
2023-01-19T10:49:00.868+01:00
fiddle

How to flatten nested array from JSON in SQL Server

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;

Using JSON_VALUE for parse column in SQL Server table

I have never worked with JSON in SQL Server before that's why need some help.
I have written a simple snippet of code:
DECLARE #json NVARCHAR(4000)
SET #json =
N'{
"id":"40476",
"tags":[
{
"id":"5f5883",
},
{
"id":"5fc8",
}
],
"type":"student",
"external_id":"40614476"
}'
SELECT
JSON_value(#json, '$.tags[0].id') as tags
In sample above I write code how get first "id" from "tags".
But how looks like script if in "tags" not 2 "id", but an unknown number this "id" and result should be in column like this:
1 5f5883
2 5fc8
You may use OPENJSON() with explicit schema to parse the $.tags JSON array:
DECLARE #json NVARCHAR(4000)
SET #json =
N'{
"id":"40476",
"tags":[
{
"id":"5f5883"
},
{
"id":"5fc8"
}
],
"type":"student",
"external_id":"40614476"
}'
SELECT id
FROM OPENJSON(#json, '$.tags') WITH (id varchar(10) '$.id')
Result:
id
------
5f5883
5fc8
If you want to get the index of each id in the $.tags JSON array, then you need a combination of OPENJSON() with default schema and JSON_VALUE():
SELECT CONVERT(int, [key]) AS rn, JSON_VALUE([value], '$.id') AS id
FROM OPENJSON(#json, '$.tags')
Result:
rn id
----------
0 5f5883
1 5fc8

Is it possible to use wildcards as an argument for OPENJSON in SQL Server?

I have a nested JSON array consisting of outer keys that are numbers, each of which contain inner arrays that I need to import into a table in SQL Server. The JSON file is setup like so:
{
"121212": {
"name": name of item,
"subject": item subject
},
"343434": {
"name": name of item,
"subject": item subject
}
}
I can use the SQL Server function OPENJSON() to import a single array without issue like so:
DECLARE #arrayVariable VARCHAR(MAX)
SELECT #arrayVariable = BulkColumn FROM OPENROWSET(BULK 'array.json', SINGLE_BLOB) JSON
INSERT INTO ArrayTable (arrayName, arraySubject)
SELECT * FROM OPENJSON(#arrayVariable, '$."121212"')
WITH (
arrayName VARCHAR(MAX) '$.name',
arraySubject VARCHAR(MAX) '$.subject'
)
The above code successfully imports array 121212 into the ArrayTable. However, I would like to know if there is a solution that can utilize wildcards as an argument for OPENJSON in order to import in all numeric array keys from the JSON array, that way they don't have to be imported individually. I have tried using wildcards but none of the formatting options I've tried have worked so far. For example:
OPENJSON(#arrayVariable, '$."[0-9]%"')
What would be the best way to import all of the numerically titled JSON arrays using OPENJSON()?
Try this
DECLARE #arrayVariable VARCHAR(MAX) = N'{
"121212": {
"name": "name of item1",
"subject": "item subject1"
},
"343434": {
"name": "name of item2",
"subject": "item subject2"
}
}'
SELECT v.arrayName, v.arraySubject
FROM OPENJSON(#arrayVariable) AS r
CROSS APPLY OPENJSON(r.value)
WITH (
arrayName VARCHAR(MAX) '$.name',
arraySubject VARCHAR(MAX) '$.subject'
) AS v
WHERE r.[key] LIKE '[0-9]%'

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