Extracting data from very nested JSON in Azure SQL - json

I have nested JSON file:
"vehicleStatusResponse": {
"vehicleStatuses": [
{
"vin": "ABC1234567890",
"triggerType": {
"triggerType": "TIMER",
"context": "RFMS",
"driverId": {
"tachoDriverIdentification": {
"driverIdentification": "123456789",
"cardIssuingMemberState": "BRA",
"driverAuthenticationEquipment": "CARD",
"cardReplacementIndex": "0",
"cardRenewalIndex": "1"
}
}
},
"receivedDateTime": "2020-02-12T04:11:19.221Z",
"hrTotalVehicleDistance": 103306960,
"totalEngineHours": 3966.6216666666664,
"driver1Id": {
"tachoDriverIdentification": {
"driverIdentification": "BRA1234567"
}
},
"engineTotalFuelUsed": 48477520,
"accumulatedData": {
"durationWheelbaseSpeedOverZero": 8309713,
"distanceCruiseControlActive": 8612200,
"durationCruiseControlActive": 366083,
"fuelConsumptionDuringCruiseActive": 3064170,
"durationWheelbaseSpeedZero": 5425783,
"fuelWheelbaseSpeedZero": 3332540,
"fuelWheelbaseSpeedOverZero": 44709670,
"ptoActiveClass": [
{
"label": "wheelbased speed >0",
"seconds": 16610,
"meters": 29050,
"milliLitres": 26310
},
{
"label": "wheelbased speed =0",
"seconds": 457344,
"milliLitres": 363350
It is already imported from Azure BLOB Storage to SQL DB and now I need to extract data from it to table. I've already used a T-SQL request to do that, but it returned me blank table with only headers:
SELECT response.*
FROM OPENROWSET (BULK 'response3.json', DATA_SOURCE = 'VCBI24', SINGLE_CLOB) as j
CROSS APPLY OPENJSON(BulkColumn)
WITH ( vehiclestatusResponse nvarchar (100), vehicleStatuses nvarchar (100), vin nvarchar (100), triggerType nvarchar (100), context nvarchar (100) and etc...) AS response
How can I handle it?
Thanks a lot for your attention!

You can supply a path with OPENJSON which allows you to drill in to nested JSON, eg
SELECT *
FROM OPENJSON( #json, '$.vehicleStatusResponse.vehicleStatuses' )
WITH (
vin VARCHAR(50) '$.vin',
triggerType VARCHAR(50) '$.triggerType.triggerType',
context VARCHAR(50) '$.triggerType.context',
driverIdentification VARCHAR(50) '$.triggerType.driverId.tachoDriverIdentification.driverIdentification',
cardIssuingMemberState VARCHAR(50) '$.triggerType.driverId.tachoDriverIdentification.cardIssuingMemberState',
receivedDateTime DATETIME '$.receivedDateTime',
engineTotalFuelUsed INT '$.engineTotalFuelUsed'
)
Full script example:
DECLARE #json VARCHAR(MAX) = '{
"vehicleStatusResponse": {
"vehicleStatuses": [
{
"vin": "ABC1234567890",
"triggerType": {
"triggerType": "TIMER",
"context": "RFMS",
"driverId": {
"tachoDriverIdentification": {
"driverIdentification": "123456789",
"cardIssuingMemberState": "BRA",
"driverAuthenticationEquipment": "CARD",
"cardReplacementIndex": "0",
"cardRenewalIndex": "1"
}
}
},
"receivedDateTime": "2020-02-12T04:11:19.221Z",
"hrTotalVehicleDistance": 103306960,
"totalEngineHours": 3966.6216666666664,
"driver1Id": {
"tachoDriverIdentification": {
"driverIdentification": "BRA1234567"
}
},
"engineTotalFuelUsed": 48477520,
"accumulatedData": {
"durationWheelbaseSpeedOverZero": 8309713,
"distanceCruiseControlActive": 8612200,
"durationCruiseControlActive": 366083,
"fuelConsumptionDuringCruiseActive": 3064170,
"durationWheelbaseSpeedZero": 5425783,
"fuelWheelbaseSpeedZero": 3332540,
"fuelWheelbaseSpeedOverZero": 44709670,
"ptoActiveClass": [
{
"label": "wheelbased speed >0",
"seconds": 16610,
"meters": 29050,
"milliLitres": 26310
},
{
"label": "wheelbased speed =0",
"seconds": 457344,
"milliLitres": 363350
}
]
}
}
]
}}}}}}}}'
SELECT *
FROM OPENJSON( #json, '$.vehicleStatusResponse.vehicleStatuses' )
WITH (
vin VARCHAR(50) '$.vin',
triggerType VARCHAR(50) '$.triggerType.triggerType',
context VARCHAR(50) '$.triggerType.context',
driverIdentification VARCHAR(50) '$.triggerType.driverId.tachoDriverIdentification.driverIdentification',
cardIssuingMemberState VARCHAR(50) '$.triggerType.driverId.tachoDriverIdentification.cardIssuingMemberState',
receivedDateTime DATETIME '$.receivedDateTime',
engineTotalFuelUsed INT '$.engineTotalFuelUsed'
)
My results:
Read more about OPENJSON here:
https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver15
A second example. With OPENJSON you can either supply the json path expression is more explicit and gives you more control particularly for nested JSON. If the JSON is relatively simple, you do not have to supply the paths, eg
DECLARE #json VARCHAR(MAX) = '{
"ptoActiveClass": [
{
"label": "wheelbased speed >0",
"seconds": 16610,
"meters": 29050,
"milliLitres": 26310
},
{
"label": "wheelbased speed =0",
"seconds": 457344,
"milliLitres": 363350
}
]
}'
SELECT *
FROM OPENJSON( #json, '$.ptoActiveClass' )
WITH (
label VARCHAR(50),
seconds INT,
meters INT,
milliLitres INT
)
SELECT *
FROM OPENJSON( #json, '$.ptoActiveClass' )
WITH (
label VARCHAR(50) '$.label',
seconds VARCHAR(50) '$.seconds',
meters VARCHAR(50) '$.meters',
milliLitres VARCHAR(50) '$.milliLitres'
)

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 Server - parse json with multiple objects

I'm parsing a JSON dataset within SQL Server and it works great with a single object, but falls down when multiple data objects are presented. I assume it's because of the CROSS APPLY statements.
Within the JSON dataset, there is only 4 records, but my current sql is returning 16 (4 duplicate sets, as there are 4 cross apply statements), but I'm not sure how to get around this?
json
{
"type": "test",
"user": {
"last_update": "2022-06-19T14:13:07.707502+00:00",
"user_id": "12345"
},
"data": [
{
"metadata": {
"start_time": "2022-06-19T00:00:00+01:00",
"end_time": "2022-06-20T00:00:00+01:00"
},
"distance_data": {
"steps": 9299,
"distance_meters": 7704.0
}
},
{
"metadata": {
"start_time": "2022-06-17T00:00:00+01:00",
"end_time": "2022-06-18T00:00:00+01:00"
},
"distance_data": {
"steps": 2546,
"distance_meters": 2143.0
}
},
{
"metadata": {
"start_time": "2022-06-16T00:00:00+01:00",
"end_time": "2022-06-17T00:00:00+01:00"
},
"distance_data": {
"steps": 4969,
"distance_meters": 4192.0
}
},
{
"metadata": {
"start_time": "2022-06-18T00:00:00+01:00",
"end_time": "2022-06-19T00:00:00+01:00"
},
"distance_data": {
"steps": 6769,
"distance_meters": 5698.0
}
}
]
}
SQL statement
SELECT
distance_meters, steps, cast(left(start_time,10) as date) startDate
FROM
OPENJSON ( #json )
WITH (
jType nvarchar(50) N'$.type',
jUser char(36) N'$.user.user_id',
data nvarchar(max) as JSON
) as a
CROSS APPLY
OPENJSON(a.data)
WITH
(
distance_data nvarchar(max) as json
) as b
CROSS APPLY
OPENJSON (b.distance_data)
WITH
(
distance_meters float,
steps int
) as c
CROSS APPLY
OPENJSON (a.data)
WITH
(
metadata nvarchar(max) as json
) as d
CROSS APPLY
OPENJSON (d.metadata)
WITH
(
start_time nvarchar(25),
end_time nvarchar(25)
) as e
ORDER BY startDate ASC;
I think you need a single APPLY operator:
SELECT j1.jType, j1.jUser, j2.*
FROM OPENJSON(#json) WITH (
jType nvarchar(50) N'$.type',
jUser char(36) N'$.user.user_id',
data nvarchar(max) as JSON
) AS j1
CROSS APPLY OPENJSON(j1.data) WITH (
start_time nvarchar(25) '$.metadata.start_time',
end_time nvarchar(25) '$.metadata.end_time',
steps numeric(10, 0) '$.distance_data.steps',
distance_meters numeric(10, 1) '$.distance_data.distance_meters'
) j2
Result:
jType
jUser
start_time
end_time
steps
distance_meters
test
12345
2022-06-19T00:00:00+01:00
2022-06-20T00:00:00+01:00
9299
7704.0
test
12345
2022-06-17T00:00:00+01:00
2022-06-18T00:00:00+01:00
2546
2143.0
test
12345
2022-06-16T00:00:00+01:00
2022-06-17T00:00:00+01:00
4969
4192.0
test
12345
2022-06-18T00:00:00+01:00
2022-06-19T00:00:00+01:00
6769
5698.0

SQL OPENJSON is not returning the values from sub-arrays

I have a JSON file that is properly formatted according to the Microsoft ISJSON function. However, it refuses to return a value from the nested array.
Here is an excerpt from the JSON file.
I want to return the following fields: id, symbol, name, and price.
I can get the first three, but the price always shows up null in the SQL query results.
JSON FILE SNIPPET:
{
"status": {
"timestamp": "2021-01-06T07:14:42.132Z",
"error_code": 0,
"error_message": null,
"elapsed": 14,
"credit_count": 1,
"notice": null,
"total_count": 4180
},
"data": [
{
"id": 1,
"name": "Bitcoin",
"symbol": "BTC",
"slug": "bitcoin",
"num_market_pairs": 9772,
"date_added": "2013-04-28T00:00:00.000Z",
"tags": [
"mineable",
"pow",
"sha-256",
"store-of-value",
"state-channels"
],
"max_supply": 21000000,
"circulating_supply": 18592156,
"total_supply": 18592156,
"platform": null,
"cmc_rank": 1,
"last_updated": "2021-01-06T07:13:02.000Z",
"quote": {
"USD": {
"price": 36248.609255662224,
"volume_24h": 225452557837159.16,
"percent_change_1h": 2.74047145,
"percent_change_24h": 19.54362963,
"percent_change_7d": 29.31750604,
"market_cap": 673939798064.3159,
"last_updated": "2021-01-06T07:13:02.000Z"
}
}
}
Here is the SQL Query that I'm using:
DECLARE #JSON VARCHAR(MAX)
SELECT #JSON = BulkColumn
FROM OPENROWSET
(BULK 'C:\TSP\output.json', SINGLE_CLOB) AS j
Select iif(ISJSON(#JSON)=1,'YES','NO') JSON_OK
Select * FROM OPENJSON (#JSON, '$.data')
WITH (
id int
,symbol varchar(20)
,[name] varchar(50)
,price float '$.data.quote.USD[0]'
)
I've tried everything I can think of to get the price to appear, but I'm missing something as it's not cooperating. Also, I set the database compatibility level to 130 as I read that could be the problem.... Still no luck.
Any help would be much appreciated.
$.data.quote.USD is not an array, it's a set of properties. It's also already inside the $.data context so should not include data in its path. Try the following instead:
select *
from openjson(#JSON, '$.data') with
(
id int
,symbol varchar(20)
,[name] varchar(50)
,price float '$.quote.USD.price'
)

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