Parse JSON Column containing multiple arrays - json

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

Related

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.

Using T-SQL to retrieve results from Json file and not iterating multiple sub-objects

Following MS documentation, I can get a simple example of loading json file to SQL results. The problems occur when I have more than one sub-object. This code will traverse all elements if at root level. Because I have 2 objects under "Purchase" I have to explicitly reference them. Is there an easier way to return results for all sub-objects? In this case I would like two rows of Order info.
Also have to hard code the filename to OPENROWSET instead of using (#file). Any ideas on syntax to pass in a variable for file?
Code
USE TempDB
DECLARE #json AS NVARCHAR(MAX)
DECLARE #file AS NVARCHAR(MAX)
SET #file = 'c:\temp\test.json';
SELECT #json = BulkColumn FROM OPENROWSET (BULK 'c:\temp\test2.json', SINGLE_CLOB) AS j
SELECT *
FROM OPENJSON ( #json )
WITH (
Number varchar(200) '$.Purchase[0].Order.Number' ,
Date datetime '$.Purchase[0].Order.Date',
Customer varchar(200) '$.Purchase[0].AccountNumber',
Quantity int '$.Purchase[0].Item.Quantity'
)
File contents:
{
"Purchase": [
{
"Order": {
"Number": "SO43659",
"Date": "2011-05-31T00:00:00"
},
"AccountNumber": "AW29825",
"Item": {
"Price": 2024.9940,
"Quantity": 1
}
},
{
"Order": {
"Number": "SO43661",
"Date": "2011-06-01T00:00:00"
},
"AccountNumber": "AW73565",
"Item": {
"Price": 2024.9940,
"Quantity": 3
}
}
]
}
Reference:
https://learn.microsoft.com/en-us/sql/relational-databases/json/convert-json-data-to-rows-and-columns-with-openjson-sql-server?view=sql-server-2017#option-2---openjson-output-with-an-explicit-structure
Thanks,
Bill
To get both rows, you need to use the second argument of the OPENJSON function, like this:
SELECT *
FROM OPENJSON ( #json,'$.Purchase' )
WITH (
Number varchar(200) '$.Order.Number' ,
Date datetime '$.Order.Date',
Customer varchar(200) '$.AccountNumber',
Quantity int '$.Item.Quantity'
)
This way you are telling SQL Server that you want all the nodes under the '$.Purchase' path (and it finds two rows). Without that, you would get all the nodes under root (and it finds just one row, the Purchase node).

How to parse JSON array string by using mssql?

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

How do you query property values with SQL Server JSON without knowing the parent object keys?

Given the following JSON in SQL Server, how do you get the "Foo" values without knowing the parent keys ("John" and "Jane")?
declare #json nvarchar(max) = '{
"John" : {
"favoriteMovie": "Star Wars",
"Foo": 42
},
"Jane" : {
"favoriteMovie": "Caddyshack",
"Foo": 66
}
}'
In SQL Server 2016 or later, you can use OPENJSON and APPLY for the number of levels in your hierarchy.
select b.[key], b.[value]
from openjson(#json) a
cross apply openjson(a.[value]) b
where b.[key] like N'Foo';
Results:
key value
Foo 42
Foo 66
Here is one way is to use XML node method to get the desired result. However, the method is odd but it would helps
select
ltrim(replace(replace(a.value('.', 'nvarchar(max)'), char(10), ''), '}', '')) json from
(
select cast('<m>'+replace(#json, ',', '</m><m>')+'</m>' as xml) as json
)a cross apply json.nodes ('/m') as json(a)
where a.value('.', 'varchar(max)') like '%foo%'
Result :
json
"Foo": 42
"Foo": 66