Advice on using OPENJSON to parse a nested array - sql

I am hoping somebody can help point me in the right direction as Iā€™m trying to parse a json file into sql using OPENJSON. I have a structure which looks like this:
DECLARE #json AS NVARCHAR(MAX) = '
[{
"id": "78",
"Version": {
"Value": "12"
},
"Names": [{
"NameId": {
"Value": "8516365"
},
"id": "328787",
"NameLinkType": {
"Value": "A"
"CommsChains": {
"Value": [[{
"com_primary": {
"Value": "Y"
},
"com_recd": {
"Value": "2020-07-07 00:00:00.000"
},
"com_ack": {
"Value": "2020-07-09 00:00:00.000"
},
}
]]
), },
},
],
}
]'
I am able to parse the majority of the JSON correctly, so for each ID I can return values such as Version or NameId. However, I am unable to return any dates in respect of com_recd or com_ack, which sit under CommsChains [Object] ā€“ Value [Array] ā€“ [0] [Array]

It looks like there are some syntactic errors in your JSON. After having them fixed, I was able to try and find the JSON paths to the date expressions to the date values. This is the SQL:
DECLARE #json AS NVARCHAR(MAX) = '
[{
"id": "78",
"Version": {
"Value": "12"
},
"Names": [{
"NameId": {
"Value": "8516365"
},
"id": "328787",
"NameLinkType": {
"Value": "A",
"CommsChains": {
"Value": [[{
"com_primary": {
"Value": "Y"
},
"com_recd": {
"Value": "2020-07-07 00:00:00.000"
},
"com_ack": {
"Value": "2020-07-09 00:00:00.000"
}
}
]]
}
}
}
]
}
]'
select * from openjson(#json, '$[0].Version');--Value 12 1
select * from openjson(#json, '$[0].Names');
select * from openjson(#json, '$[0].Names[0]');
select * from openjson(#json, '$[0].Names[0].NameLinkType');
select * from openjson(#json, '$[0].Names[0].NameLinkType.CommsChains');
select * from openjson(#json, '$[0].Names[0].NameLinkType.CommsChains.Value');
select * from openjson(#json, '$[0].Names[0].NameLinkType.CommsChains.Value[0]');
select * from openjson(#json, '$[0].Names[0].NameLinkType.CommsChains.Value[0][0]');
select * from openjson(#json, '$[0].Names[0].NameLinkType.CommsChains.Value[0][0].com_recd'); --selecting path for com_recd
select * from openjson(#json, '$[0].Names[0].NameLinkType.CommsChains.Value[0][0].com_ack'); --selecting path for com_ack
Herein I show the selects to the different parts of your JSON. The arrays'content are always referenced as [0] as its always the first index to select here.
For more information on JSON paths on the SQL server look here

Related

Parse Google API JSON file to rows and columns with OPENJSON in T-SQL

So I am trying to create a query than can handle a json file that we get with a data factory web request from the Google Analytics API 4 and store the result in an Azure sql table. The following query is the closest I got.
The dimension and metric headers seems to be column names and the values in the rows part should be the rows.
DECLARE #jsonexample NVARCHAR(MAX) =
N'{
"dimensionHeaders": [
{
"name": "date"
},
{
"name": "country"
}
],
"metricHeaders": [
{
"name": "totalUsers",
"type": "TYPE_INTEGER"
}
],
"rows": [
{
"dimensionValues": [
{
"value": "20230207"
},
{
"value": "Netherlands"
}
],
"metricValues": [
{
"value": "3"
}
]
},
{
"dimensionValues": [
{
"value": "20230208"
},
{
"value": "Netherlands"
}
],
"metricValues": [
{
"value": "2"
}
]
},
{
"dimensionValues": [
{
"value": "20230208"
},
{
"value": "United States"
}
],
"metricValues": [
{
"value": "1"
}
]
}
]
}'
DECLARE #jsonexample2 NVARCHAR(MAX) = (SELECT [value] FROM OPENJSON(#jsonexample) where [key]= 'rows' )
SELECT *
from OPENJSON(#jsonexample2)
This blog seemed to have a good explanation but I still not got it working.
https://levelup.gitconnected.com/how-to-easily-parse-and-transform-json-in-sql-server-c0b091a964de
You can shred it down to something like this:
DECLARE #jsonexample NVARCHAR(MAX) =
N'{
"dimensionHeaders": [
{
"name": "date"
},
{
"name": "country"
}
],
"metricHeaders": [
{
"name": "totalUsers",
"type": "TYPE_INTEGER"
}
],
"rows": [
{
"dimensionValues": [
{
"value": "20230207"
},
{
"value": "Netherlands"
}
],
"metricValues": [
{
"value": "3"
}
]
},
{
"dimensionValues": [
{
"value": "20230208"
},
{
"value": "Netherlands"
}
],
"metricValues": [
{
"value": "2"
}
]
},
{
"dimensionValues": [
{
"value": "20230208"
},
{
"value": "United States"
}
],
"metricValues": [
{
"value": "1"
}
]
}
]
}'
;with cols as (
select cast([key] as int) AS k, JSON_VALUE(value, '$.name') AS v
from openjson(#jsonexample, '$.dimensionHeaders') x
)
, metrics as (
select cast([key] as int) AS k, JSON_VALUE(value, '$.name') AS v
from openjson(#jsonexample, '$.metricHeaders') x
)
select CAST(x.[key] AS INT) AS id, c.v AS dimName, JSON_VALUE(dim.value, '$.value') AS dimValue
, m.v AS metName, JSON_VALUE(metr.value, '$.value') AS metValue
from openjson(#jsonexample, '$.rows') x
cross apply openjson(x.value, '$.dimensionValues') dim
cross apply openjson(x.value, '$.metricValues') metr
inner join cols c
ON c.k = dim.[key]
inner join metrics m
ON m.k = metr.[key]
Then you can probably figure out the rest.
Here is a bit code that will dynamically parse the metrics and dimension for Google Analytics. It should give you a good starting point :)
SELECT TOP 1 #json = JSON_QUERY(RawJson, '$.reports[0].columnHeader')
FROM TableName
SET #WithClause =
(
SELECT STRING_AGG(Line, ',')
FROM
(
SELECT REPLACE(r.value, 'ga:', '')+' '+CASE
WHEN r.value = 'ga:DATE' THEN 'DATE'
ELSE 'NVARCHAR(255)'
END+' '+'''$.dimensions['+r.[key]+']''' AS Line
FROM OPENJSON(#json, '$.dimensions') AS r
UNION ALL
SELECT REPLACE(JSON_VALUE(r.value, '$.name'), 'ga:', '')+' '+CASE
WHEN JSON_VALUE(r.value, '$.type') = 'TIME' THEN 'FLOAT'
WHEN JSON_VALUE(r.value, '$.type') = 'CURRENCY' THEN 'DECIMAL(9,2)'
ELSE JSON_VALUE(r.value, '$.type')
END+' '+'''$.metrics[0].values['+r.[key]+']'''
FROM OPENJSON(#json, '$.metricHeader.metricHeaderEntries') AS r
) AS a
);
SET #Query = '
SELECT d.*
INTO #temp_table
FROM TableNAme AS cm
CROSS APPLY OPENJSON(RawJson, ''$.reports[0].data.rows'') WITH ( '+#WithClause+ ') AS d';
--PRINT #Query;
EXECUTE (#Query);

Is there an UPDATE equivalent command to SELECT json data

Is there a way to update the data retrieved from the below select (in this case, change "MS220" to something else)? It's difficult enough to do select from JSON in some cases. I'm not sure how to update just a single element.
CREATE TABLE JData (
JsonData nvarchar(max)
)
INSERT INTO JData
(JsonData)
VALUES
('[
{
"Categories": [
{
"QuerySourceNames": [
"QAsset"
],
"Id": "eceae85a-ffc6-49f4-8f6a-78ce2b4b274e",
"Name": "emsdba"
}
],
"Id": "525b4f07-0f67-43ac-8070-a0e6c1ceb1b9",
"Name": "MS220"
}
]')
SELECT
ParamName
FROM [dbo].[JData] jsonData
CROSS APPLY OPENJSON (jsonData)
WITH
(
Categories nvarchar(max) AS json,
Id uniqueidentifier,
ParamName varchar(10) '$.Name'
);
Try JSON_MODIFY() with the path '$[0].Name'
UPDATE d
SET jsonData = JSON_MODIFY(jsonData, '$[0].Name', 'New Value')
FROM [dbo].[JData] d
Results:
[
{
"Categories": [
{
"QuerySourceNames": [
"QAsset"
],
"Id": "eceae85a-ffc6-49f4-8f6a-78ce2b4b274e",
"Name": "emsdba"
}
],
"Id": "525b4f07-0f67-43ac-8070-a0e6c1ceb1b9",
"Name": "New Value"
}
]
db<>fiddle here

how to measure length of array inside array in json file using SQL OPENJSON function with cross APPLY

"table": [
{
"name": "Emergency",
"columns": [
{
"name": "ab",
"type": "long"
},
{
"name": "cd",
"type": "long"
},
{
"name": "ef",
"type": "long"
},
{
"name": "gh",
"type": "long"
},
],
"rows": [
[
0.55865,
2.0966,
0.4280,
1.4389
],
[
0.42490,
1.5723,
0.3601,
0.8031
]
]
}
]
}
so in this json, array object inside rows can be multiple(more than 2). so how to count object of array sinde 'rows' using OPENJSON SQL Server Function.
what I have done is, store this json into temporary table #TempAb and then
DECLARE #ab VARCHAR(MAX);
DECLARE #cd VARCHAR(MAX);
DECLARE #ef VARCHAR(MAX);
DECLARE #gh VARCHAR(MAX);
SELECT #ab=ab,#cd=cd,#ef=ef,#gh=gh from #TempAb as ab CROSS APPLY
OPENJSON(ab.RawData, '$.table') WITH
(
ab Varchar(MAX) '$.rows[0][0],
cd Varchar(Max) '$.rows[0][1],
ef Varchar(Max) '$.rows[0][2],
gh Varchar(Max) '$.rows[0][3]
);
so this query only returns data of the first object of rows, but I want data of all objects of rows. so I know the count then iterate this inside while loop
Solution:
SET #cnt_total = (SELECT COUNT(O.[key]) FROM (VALUES(#jsonvalue))V(J)
CROSS APPLY OPENJSON(V.J)
WITH([Object] nvarchar(MAX) '$.table[0].rows' AS JSON) M
CROSS APPLY OPENJSON(M.[Object]) O)
First you have to start with valid JSON, then something like this:
declare #json nvarchar(max) = N'
{
"table": [
{
"name": "Emergency",
"columns": [
{
"name": "ab",
"type": "long"
},
{
"name": "cd",
"type": "long"
},
{
"name": "ef",
"type": "long"
},
{
"name": "gh",
"type": "long"
}
],
"rows": [
[
0.55865,
2.0966,
0.4280,
1.4389
],
[
0.42490,
1.5723,
0.3601,
0.8031
]
]
}
]
}
'
select count(*) from
OPENJSON(#json, '$.table[0].rows') d

BigQuery concat nested array json

I have data that looks like
{
"Attributes": [
{
"values": [
{
"value": "20003"
},
{
"value": "30075"
},
{
"value": "40060"
}
],
"name": "price"
}
],
"attr2" : "val"
}
The output I want is concat all the values in the nested json array
price, "20003, 30075, 40060"
I tried some queries but failed to get the correct output.
You can use JSON_EXTRACT_ARRAY and ARRAY_TO_STRING:
WITH test_json AS (
SELECT
'''{
"Attributes": [
{
"values": [
{
"value": "20003"
},
{
"value": "30075"
},
{
"value": "40060"
}
],
"name": "price"
}
],
"attr2" : "val"
}''' AS json_string
),
values_concatenated AS (
SELECT ARRAY_TO_STRING(
ARRAY(
SELECT JSON_VALUE(json_values, '$.value')
FROM UNNEST((SELECT JSON_EXTRACT_ARRAY(json_string, '$.Attributes[0].values') AS json_values FROM test_json)) as json_values
),
', '
) as values
)
SELECT
(select json_value(json_string, '$.Attributes[0].name') from test_json),
(select values from values_concatenated)

SQL Query to get the count of Json Array

I have the below Json object. How do I get the count of Object Array.
{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC"
},
{
"ID": 11,
"Name": "ABCD"
},
]
}]}
I tried the below query but seems JSON_Length was not available.
SELECT ModelName,
JSON_LENGTH(JsonData, '$.Model[0].Object')
FROM TabA
The expected output should be
ModelName COUNT
Test Model 2
If you have valid JSON (at the moment you have a trailing comma (,_ after one of your closing braces (})), then you could use OPENJSON and COUNT:
DECLARE #JSON nvarchar(MAX) = N'{
"Model": [
{
"ModelName": "Test Model",
"Object": [
{
"ID": 1,
"Name": "ABC"
},
{
"ID": 11,
"Name": "ABCD"
}
]
}]}';
SELECT M.ModelName,
COUNT(O.[key]) AS [Count]
FROM (VALUES(#JSON))V(J)
CROSS APPLY OPENJSON(V.J)
WITH(ModelName varchar(20) '$.Model[0].ModelName',
[Object] nvarchar(MAX) '$.Model[0].Object' AS JSON) M
CROSS APPLY OPENJSON(M.[Object]) O
GROUP BY M.ModelName;