How to parse JSON array string by using mssql? - json

I've had created the function ParseJson below :
create function ParseJson(
#json nvarchar(max))
returns #tempTable table (topKey nvarchar(max), [Key] nvarchar(max), [Value] nvarchar(max))
as
begin
insert
#tempTable
select
x.[key] topKey
, y.[key]
, y.[value]
from openjson(#json) x
cross apply openjson(x.[value]) y
return
end
-- execute
select * from ParseJson(#json)
I defined a variable #json which is not an array string :
set #json =
N'{
"Chapter":
{
"Section":"1.1"
, "Title":"Hello world."
}
}'
Then I execute the query I will get the result :
If I reset the variable #json which is an array string :
declare #json nvarchar(max)
set #json =
N'{
"Chapter":[
{
"Section":"1.1"
, "Title":"Hello world."
}
,
{
"Section":"1.2"
, "Title":"Be happy."
}
]
}'
Then I execute the query I will get the result :
I hope I can get the result below :
I don't know the result that I expectation is reasonable?
How can I adjust the function to meet the expectation?

If I understand your question correctly, next statement is one possible approach to get your results. It's an example that shows how to get array keys and values (I've added additional Something key). What you need is one additional CROSS APPLY.
DECLARE #json nvarchar(max)
SET #json =
N'{
"Chapter":[
{
"Section":"1.1",
"Title":"Hello world.",
"Something":"Something value"
}
,
{
"Section":"1.2",
"Title":"Be happy."
}
]
}'
SELECT
x.[key] topKey,
z.[key],
z.[value]
FROM OPENJSON(#json) x
CROSS APPLY (SELECT * FROM OPENJSON(x.[value])) y
CROSS APPLY (SELECT * FROM OPENJSON(y.[value])) z
Output:
topKey key value
Chapter Section 1.1
Chapter Title Hello world.
Chapter Something Something value
Chapter Section 1.2
Chapter Title Be happy.
Update:
If your JSON has different types of nested objects, approach will be different:
DECLARE #json nvarchar(max)
set #json = N'{
"Chapter": [
{
"Section":"1.1",
"Title":"Hello world.",
"Something":"Something value"
},
{
"Section":"1.2",
"Title":"Be happy."
}
],
"Author": { "name":"Annie" , "sex":"Female" }
}'
SELECT
x.[key] topKey,
z.[key] [Key],
z.[value] AS [Value]
FROM OPENJSON(#json) x
CROSS APPLY (SELECT * FROM OPENJSON(x.[value])) y
CROSS APPLY (SELECT * FROM OPENJSON(y.[value])) z
WHERE y.[type] = 5
UNION ALL
SELECT
x.[key] topKey,
y.[key] [Key],
y.[value] AS [Value]
FROM OPENJSON(#json) x
CROSS APPLY (SELECT * FROM OPENJSON(x.[value])) y
WHERE y.[type] <> 5
Output:
topKey Key Value
Chapter Section 1.1
Chapter Title Hello world.
Chapter Something Something value
Chapter Section 1.2
Chapter Title Be happy.
Author name Annie
Author sex Female

Related

Parse JSON Column containing multiple arrays

I'm trying to get this result but I'm not getting it. I've searched the forum but I can't find a solution to my problem. Can you help me and explain what is wrong with my query?
thanks.
JSON
{
"items": [
{
"id": 40054,
"categories": [
[
28,
168,
53
]
]
}
]
}
SQL
Declare #JSON varchar(max)
SELECT #JSON=BulkColumn
FROM OPENROWSET (BULK 'C:\temp\test.json', SINGLE_CLOB) import
INSERT INTO dbo.TABLE
SELECT
metadata.[id],
categories.[categories1],
categories.[categories2],
categories.[categories3],
getdate() as [dt_imp] FROM OPENJSON (#JSON)
WITH(
[items] NVARCHAR(MAX) AS JSON
) AS Data
OUTER APPLY OPENJSON([Data].[items])
WITH(
[id] NVARCHAR(MAX),
[categories] NVARCHAR(MAX) AS JSON
) AS metadata
OUTER APPLY OPENJSON([Metadata].[categories])
WITH(
[categories1] NVARCHAR(MAX),
[categories2] NVARCHAR(MAX),
[categories3] NVARCHAR(MAX)
) AS categories
result I want
id
categories1
categories2
categories3
40054
28
168
53
Perhaps this will help.
Updated to allow multiple arrays within the array.
Select ID = json_value(A.value,'$.id')
,cat1 = json_value(B.value,'$[0]')
,cat2 = json_value(B.value,'$[1]')
,cat3 = json_value(B.value,'$[2]')
From OpenJSON(#JSON,'$.items') A
Cross Apply OpenJSON(A.value,'$.categories') B
Results
ID cat1 cat2 cat3
40054 28 168 53

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 to Parse JSON Arrays to SQL SERVER 2016

I'm looking to create a table of data (see "Output Table") from the below JSON. I can't seem to get to the "final mile". Can someone show me the way to properly handle the parsing of the arrays into the desired output table?
Thanks! Russ
Desired Output Table
Output I am getting
declare #json nvarchar(max) =
'{
"totalCount": 1,
"nextPageKey": null,
"result": [
{
"metricId": "builtin:host.cpu.usage",
"data": [
{
"dimensions": [
"HOST-CCCC3F95D7CE56"
],
"dimensionMap": {
"dt.entity.host": "HOST-CCCC3F95D7CE56"
},
"timestamps": [
1612634400000,
1612645200000,
1612656000000
],
"values": [
0.37900028935185187,
0.3709309895833333,
0.5088975694444444
]
}
]
}
]
}'
Select TableA.totalCount, TableResult.metricId,
TableDim.*
FROM OPENJSON(#json)
WITH(
totalCount int,
result NVARCHAR(MAX) as JSON
) as TableA
CROSS APPLY OPENJSON(TableA.result)
WITH(
metricId VARCHAR(100),
data NVARCHAR(MAX) as JSON
)TableResult
CROSS APPLY OPENJSON(TableResult.data)
WITH(
dimensions NVARCHAR(MAX) as JSON,
timestamps NVARCHAR(MAX) as JSON,
[values] NVARCHAR(MAX) as JSON
)TableDim
Hard to know what the correlation is between timestamps and values, but I've assumed it's by array index.
Is dimensions always a single value in the array? I have assumed so.
You then need to use OPENJSON (and no WITH block) to break out the two arrays, and join them together on index, which is supplied in key:
Select TableA.totalCount, TableResult.metricId,
TableDim.dimensions, Vals.*
FROM OPENJSON(#json)
WITH(
totalCount int,
result NVARCHAR(MAX) as JSON
) as TableA
CROSS APPLY OPENJSON(TableA.result)
WITH(
metricId VARCHAR(100),
data NVARCHAR(MAX) as JSON
)TableResult
CROSS APPLY OPENJSON(TableResult.data)
WITH(
dimensions NVARCHAR(MAX) '$.dimensions[0]',
timestamps NVARCHAR(MAX) as JSON,
[values] NVARCHAR(MAX) as JSON
)TableDim
CROSS APPLY (
SELECT tm.value AS Timestamp, v.value AS Value
FROM OPENJSON(TableDim.timestamps) tm
JOIN OPENJSON(TableDim.[values]) v ON v.[key] = tm.[key]
) Vals

Receiving 'NULL' return from OPENJSON() when reading JSON data

I need to pull the data from a seperate JSON File into my SQL table however , I cant seem to come right with a result.
It keeps returning 'NULL' and I dont quite understand why - I suspect it is the JSON path listed in the OPENJSON() but cant seem to come right.
JSON
{
"isMoneyClient": false,
"showPower": true,
"removeGamePlay": true,
"visualTweaks": {
"0": {
"value": true,
"name": "Clock"
},
"1": {
"value": true,
"name": "CopperIcon"
}
}
}
SQL
DECLARE #JSON VARCHAR(MAX)
SELECT #JSON = BulkColumn
FROM OPENROWSET
(BULK 'C:\config.json', SINGLE_CLOB)
AS A
UPDATE dbo.CommonBaseline
SET CommonBaseline.Config_isMoneyClient = isMoneyClient ,
CommonBaseline.Config_showPower = showPower ,
CommonBaseline.Config_removeGamePlay = removeGamePlay
FROM OPENJSON (#JSON)
WITH (
isMoneyClient Varchar (50),
showPower Varchar (50),
removeGamePlay Varchar (50)
)
AS REQUESTED I HAVE BELOW THE COMMON BASELINE SCHEME
CREATE TABLE CommonBaseline (
ServerID int NOT NULL PRIMARY KEY,
Config_isMoneyClient varchar(255) ,
Config_showPower varchar(255) ,
Config_removeGamePlay varchar(255)
);
Update:
This is an attempt to improve this answer. When you miss a column path definition in OPENJSON() call using WITH clause, matches between the keys in JSON expression and the column names are case sensitive. This is another possible reason for unexpected NULL results, because you don't use column paths in your OPENJSON() call.
Example:
DECLARE #json nvarchar(max) = N'{
"isMoneyClient": false,
"showPower": true,
"removeGamePlay": true,
"visualTweaks": {
"0": {
"value": true,
"name": "Clock"
},
"1": {
"value": true,
"name": "CopperIcon"
}
}
}'
-- NULL results.
-- The column names (IsMoneyClient) and
-- the keys in JSON expression (isMoneyClient) don't match.
SELECT *
FROM OPENJSON (#json) WITH (
IsMoneyClient Varchar (50),
ShowPower Varchar (50),
RemoveGamePlay Varchar (50)
)
-- Expected results. The correct column paths are used
SELECT *
FROM OPENJSON (#json) WITH (
IsMoneyClient Varchar(50) '$.isMoneyClient',
ShowPower Varchar(50) '$.showPower',
RemoveGamePlay Varchar(50) '$.removeGamePlay'
)
Original answer:
One possible explanation for your unexpected result is the fact, that in some cases, even if your JSON content is invalid, OPENJSON() reads only part of this content. I'm able to reproduce this with the next example:
Statement:
-- JSON
-- Valid JSON is '[{"name": "A"},{"name": "B"}]'
DECLARE #json nvarchar(max) = N'
{"name": "A"},
{"name": "B"}
'
-- Read JSON content
SELECT * FROM OPENJSON(#json, '$')
SELECT * FROM OPENJSON(#json, '$') WITH (
[name] nvarchar(100) '$.name'
)
Output (OPENJSON() reads only {"name": "A"} part of the JSON input):
----------------
key value type
----------------
name A 1
----
name
----
A
One solution here is to check your JSON content with ISJSON:
IF ISJSON(#json) = 1 PRINT 'Valid JSON' ELSE PRINT 'Not valid JSON';
If it is possible, try to fix the input JSON:
Statement:
-- JSON
-- Valid JSON is '[{"name": "A"},{"name": "B"}]'
DECLARE #json nvarchar(max) = N'
{"name": "A"},
{"name": "B"}
'
-- Read JSON content
SELECT * FROM OPENJSON(CONCAT('[', #json, ']'), '$') WITH (
[name] nvarchar(100) '$.name'
)
Output:
----
name
----
A
B
Please check your table schema of CommonBaseline. Datatype of columns Config_isMoneyClient, Config_showPower, Config_removeGamePlay
As per below scenario it is working fine
DECLARE #JSON VARCHAR(MAX)
SELECT #JSON = BulkColumn
FROM OPENROWSET
(BULK 'e:\config.json', SINGLE_CLOB)
AS A
or
SELECT #JSON = '{
"isMoneyClient": false,
"showPower": true,
"removeGamePlay": true,
"visualTweaks": {
"0": {
"value": true,
"name": "Clock"
},
"1": {
"value": true,
"name": "CopperIcon"
}
}
}'
declare #CommonBaseline as Table
( id int,
Config_isMoneyClient bit,
Config_showPower bit,
Config_removeGamePlay bit )
insert into #CommonBaseline ( id ) values ( 1 ) ---- please check your table CommonBaseline as atleast 1 record to update the same
UPDATE #CommonBaseline
SET Config_isMoneyClient = isMoneyClient ,
Config_showPower = showPower ,
Config_removeGamePlay = removeGamePlay
FROM OPENJSON (#JSON)
WITH (
isMoneyClient Varchar (50),
showPower Varchar (50),
removeGamePlay Varchar (50)
)
select * from #CommonBaseline
Note: Please check you already have your row in CommonBaseline. Since you are using update.

Consume JSON via OPENJSON and save in table - SQL Server 2016

We recently upgraded our backend DB from SQL Server 2012 to SQL Server 2016 which I realized supports JSON objects. One of our web services return data in the below format, and I am trying to consume it directly using OPENJSON function.
{
"RESULT_1": {
"columns": ["col1", "col2", "col3", "col4"],
"data": [
["0", null, "12345", "other"],
["1", "a", "54321", "MA"],
["0", null, "76543", "RI"]
]
}
}
I want to make sure that I read the column names from the "columns section" and make sure that the correct data is read and pushed in SQL Server 2016.
DECLARE #Json_Array2 nvarchar(max) = '{
"RESULT_1": {
"columns": ["col1", "col2", "col3", "col4"],
"data": [
["0", null, "12345", "other"],
["1", "a", "54321", "MA"],
["0", null, "76543", "RI"]
]
} }';
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.columns')
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.data')
With above statements, I am able to extract the columns and the data array. Would it be possible to dump this data in a flat table (already existing) with the same column names?
I am able to see all the values of one particular row by:
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.data[0]')
key value
0 0
1 NULL
2 12345
3 other
Any pointers are appreciated.
Thank you
If I understand correctly you are trying to extract all elements from the "data" array
Col1 Col2 Col3 Col4
0 NULL 12345 other
1 a 54321 MA
0 NULL 76543 RI
Suppose your Json Structure will not change you can achieve that using the following query:
SET NOCOUNT ON
IF OBJECT_ID ('tempdb..##ToPvt') IS NOT NULL DROP TABLE ##ToPvt
DECLARE #Cmd NVARCHAR(MAX)=''
DECLARE #Table TABLE (Col1 nvarchar(100), Col2 nvarchar(100), Col3 nvarchar(100) , Col4 nvarchar(100))
DECLARE #Json_Array2 nvarchar(max) = '{
"RESULT_1": {
"columns": ["col1", "col2", "col3", "col4"],
"data": [
["0", null, "12345", "other"],
["1", "a", "54321", "MA"],
["0", null, "76543", "RI"]
]
} }';
;WITH Cols ([Key], [Value]) as
(
SELECT [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.columns')
)
, [Rows] as
(
SELECT ROW_NUMBER () OVER (ORDER BY [Key]) Seq, [key], value
FROM OPENJSON(#Json_Array2,'$.RESULT_1.data')
)
,ToPvt as
(
SELECT c.[Key]+1 Cols, x.Value , 'col'+CONVERT(VARCHAR(10),ROW_NUMBER () OVER (PARTITION BY c.Value ORDER BY t.[Key])) Seq
FROM [Rows] t
INNER JOIN Cols C
ON t.[key] = c.[key]
CROSS APPLY OPENJSON(t.value) X
)
SELECT *
INTO ##ToPvt
FROM ToPvt
;WITH Final (Cmd) as
(
SELECT DISTINCT 'SELECT [col1], [col2], [col3],[col4] FROM ##ToPvt
PIVOT
(
MAX([Value]) FOR Seq IN ([col1], [col2], [col3],[col4])
) T
WHERE Cols = '+CONVERT(VARCHAR(10),Cols)+'
;'
FROM ##ToPvt
)
SELECT #Cmd += Cmd
FROM Final
INSERT INTO #Table
EXEC sp_executesql #Cmd
SELECT *
FROM #Table