Parsing with OPENJSON - json

I am trying to take the response from a GET call and load it into SQL Server via OPENJSON, but I'm having trouble parsing the response.
DECLARE #json NVARCHAR(MAX);
SET #json = N'{
"LookupServiceType": "GetAssetValues",
"Items": [
{
"id": "19676",
"value": "{\"AssetCode\":\"TDAACC\",\"Symbol\":null,\"Issue\":\"ACCOUNT #49\",\"Issuer\":\"TD AMERITRADE\"}"
},
{
"id": "19677",
"value": "{\"AssetCode\":\"RE100\",\"Symbol\":null,\"Issue\":\"APN: 057\",\"Issuer\":\"SAN ANTONIO TX 78212\"}"
},
{
"id": "19908",
"value": "{\"AssetCode\":\"NALIPO\",\"Symbol\":null,\"Issue\":\"POLICY # L0472\",\"Issuer\":\"NATIONWIDE LIFE\"}"
}
]
}';
I've tried many variations, but I just can't get it right. Here are a few things I've tried. Any suggestions would be appreciated.
SELECT *
FROM OPENJSON(#json, '$.Items')
WITH (
Items int '$.id',
value NVARCHAR(MAX) '$.value',
AssetCode NVARCHAR(50) '$.value.AssetCode',
Symbol NVARCHAR(50) '$.value.Symbol',
Issue NVARCHAR(50) '$.value.Issue',
Issuer NVARCHAR(50) '$.value.Issuer'
);
SELECT id,AssetCode,Symbol,Issue, Issuer
FROM OPENJSON(#json)
WITH (
Items NVARCHAR(MAX) '$.Items' AS JSON
)
OUTER APPLY OPENJSON(Items)
WITH (
id INT 'strict $.id',
value NVARCHAR(MAX) '$.value' AS JSON
)
OUTER APPLY OPENJSON(value)
WITH (
AssetCode NVARCHAR(50) '$.AssetCode',
Symbol NVARCHAR(50) '$.Symbol',
Issue NVARCHAR(50) '$.Issue',
Issuer NVARCHAR(50) '$.Issuer'
);

I think you're looking for this. There weren't any changes needed to the JSON
select j1.LookupServiceType, j2.id, j3.AssetCode, j3.Symbol, j3.Issue, j3.Issuer
from
OPENJSON(#json) WITH (LookupServiceType nvarchar(4000),
Items NVARCHAR(MAX) '$.Items' AS JSON) j1
outer APPLY
OPENJSON(Items) WITH (id INT 'strict $.id',
value NVARCHAR(MAX) '$.value' /*AS JSON*/) j2
outer apply
OPENJSON([value]) WITH (AssetCode NVARCHAR(50) '$.AssetCode',
Symbol NVARCHAR(50) '$.Symbol',
Issue NVARCHAR(50) '$.Issue',
Issuer NVARCHAR(50) '$.Issuer') j3;
Results
LookupServiceType id AssetCode Symbol Issue Issuer
GetAssetValues 19676 TDAACC NULL ACCOUNT #49 TD AMERITRADE
GetAssetValues 19677 RE100 NULL APN: 057 SAN ANTONIO TX 78212
GetAssetValues 19908 NALIPO NULL POLICY # L0472 NATIONWIDE LIFE

The issue is the format of your JSON string, you have superfluous escapes and "".
Your first query works fine once the JSON string is cleaned up:
DECLARE #json NVARCHAR(MAX);
SET #json = N'{
"LookupServiceType": "GetAssetValues",
"Items": [
{
"id": "19676",
"value": {"AssetCode":"TDAACC","Symbol":null,"Issue":"ACCOUNT #49","Issuer":"TD AMERITRADE"}
},
{
"id": "19677",
"value": {"AssetCode":"RE100","Symbol":null,"Issue":"APN: 057","Issuer":"SAN ANTONIO TX 78212"}
},
{
"id": "19908",
"value": {"AssetCode":"NALIPO","Symbol":null,"Issue":"POLICY # L0472","Issuer":"NATIONWIDE LIFE"}
}
]
}';
SELECT *
FROM OPENJSON(#json, '$.Items')
WITH (
Items int '$.id',
AssetCode NVARCHAR(50) '$.value.AssetCode',
Symbol NVARCHAR(50) '$.value.Symbol',
Issue NVARCHAR(50) '$.value.Issue',
Issuer NVARCHAR(50) '$.value.Issuer'
);
Results:
Items AssetCode Symbol Issue Issuer
19676 TDAACC NULL ACCOUNT #49 TD AMERITRADE
19677 RE100 NULL APN: 057 SAN ANTONIO TX 78212
19908 NALIPO NULL POLICY # L0472 NATIONWIDE LIFE
Alternatively if you don't wish to fix your JSON you could use the following statement:
SELECT *
FROM OPENJSON(REPLACE(REPLACE(Replace(#json, '\', ''), '"{', '{'), '}"', '}'), '$.Items')
WITH (
Items int '$.id',
value NVARCHAR(MAX) '$.value',
AssetCode NVARCHAR(50) '$.value.AssetCode',
Symbol NVARCHAR(50) '$.value.Symbol',
Issue NVARCHAR(50) '$.value.Issue',
Issuer NVARCHAR(50) '$.value.Issuer'
);

Related

SQL Server Import JSON subgroup array

I have a code excerpt of a SQL Server script that reads the JSON files and inserts into a table.
I am stuck with a section of the file that is an array. Example:
"top": 19.2743,
"bottom": 20.3115,
"left": 0.2878,
"right": 1.7038,
"isInternalToDevice": false,
"numberOfLegs": 3,
"legs": [
3,
2
],
I am trying to get the "legs, as "3,2"
Here is the code that reads everything else - just need the legs (to stand on :-) )
DECLARE #JSONRoot VARCHAR(50)
SET #JSONRoot = '$._embedded.symbols'
SELECT
[id],
[description],
[displayCategoryProgrammaticName],
[displayCategoryProgrammaticNameDisplay],
[manufacturer],
[model],
[modelqualifier],
[ProgrammaticName],
[Type],
[Position],
[Label],
[ReceptacleType],
[ConnectorType],
[NumberOfLegs]
FROM OPENROWSET (BULK 'D:\Test\TestFileSymbols.json', SINGLE_CLOB) as j
CROSS APPLY OPENJSON(BulkColumn, ''+#JSONRoot+'')
WITH
(
[id] UNIQUEIDENTIFIER,
[description] VARCHAR(200),
[displayCategoryProgrammaticName] VARCHAR(50),
[displayCategoryProgrammaticNameDisplay] VARCHAR(50),
[manufacturer] VARCHAR(100),
[model] VARCHAR(100),
[modelqualifier] VARCHAR(100),
[openings] NVARCHAR(MAX)'$.openings' AS JSON
)
OUTER APPLY OPENJSON(openings)
WITH (
[ProgrammaticName] VARCHAR(100) N'$.programmaticName',
[Type] VARCHAR(100) N'$.type',
[Position] VARCHAR(100) N'$.side',
[Label] VARCHAR(30) N'$.label',
[ReceptacleType] VARCHAR(100) N'$.receptacleType',
[ConnectorType] VARCHAR(50) N'$.connectorType',
[NumberOfLegs] INT N'$.numberOfLegs',
JSON_QUERY([openings], N'$.legs') AS Legs
)
You'll need to return $.legs using the AS JSON option and then you can query that further, if needed, to transform it into a comma-delimited string by way of STRING_AGG(), e.g.:
declare #json nvarchar(max) =
N'{
"top": 19.2743,
"bottom": 20.3115,
"left": 0.2878,
"right": 1.7038,
"isInternalToDevice": false,
"numberOfLegs": 3,
"legs": [
3,
2
]
}';
select *
from openjson(#json) with (
[NumberOfLegs] int N'$.numberOfLegs',
[Legs] nvarchar(max) N'$.legs' as JSON
) shredded
outer apply (
select string_agg(value, ',') from openjson(Legs)
) joined(Leggy);
Which returns the results:
NumberOfLegs
Legs
Leggy
3
[ 3, 2 ]
3,2

OPENJSON on JSON with double empty array

I have this JSON
{
"OrderHeader": [[
{
"OrderID": 100,
"CustomerID": 2000,
"OrderDetail":
{
"ProductID":2000,
"UnitPrice":350
}
}
]]
}
After the OrderHeader there is two [[.
I am not able to parse this JSON using the OPENJSON TSQL Command.
If it was just one [ then i would parse like
DECLARE #JSONData varchar(MAX) = '
{
"OrderHeader": [
{
"OrderID": 100,
"CustomerID": 2000,
"OrderDetail":
{
"ProductID": 2000,
"UnitPrice": 350
}
}
]
}
'
SELECT
OrderID, CustomerID, ProductID, UnitPrice
FROM OPENJSON (#JSONData)
WITH (
OrderHeader nvarchar(MAX) AS JSON
) AS Orders
CROSS APPLY OPENJSON(Orders.OrderHeader)
WITH
(
OrderID INT,
CustomerID INT,
OrderDetail nvarchar(MAX) AS JSON
) AS OrderDetail
CROSS APPLY OPENJSON(OrderDetail.OrderDetail)
WITH
(
ProductID INT,
UnitPrice INT
) ;
Please help to Parse with its with TWO [ as in the first sample
Thank you.
One method would be to double up the OPENJSONs, though I would personally suggest, if you can, fixing the JSON:
DECLARE #JSONData nvarchar(MAX) = N'
{
"OrderHeader": [[
{
"OrderID": 100,
"CustomerID": 2000,
"OrderDetail":
{
"ProductID": 2000,
"UnitPrice": 350
}
}
]]
}';
SELECT V.OrderID,
V.CustomerID,
OD.ProductID,
OD.UnitPrice
FROM OPENJSON(#JSONData)
WITH (OrderHeader nvarchar(MAX) AS JSON) OJ
CROSS APPLY OPENJSON(OJ.OrderHeader) OH
CROSS APPLY OPENJSON(OH.[value])
WITH (OrderID int,
CustomerID int,
OrderDetail nvarchar(MAX) AS JSON) V
CROSS APPLY OPENJSON(V.OrderDetail)
WITH (ProductID int,
UnitPrice int) OD;
The statement depends on the structure of the parsed JSON ( ... I hope I understand it correctly) and you may simplify your statement using only two OPENJSON() calls:
JSON (with one additional item in the nested JSON array):
DECLARE #JSONData varchar(MAX) = '
{
"OrderHeader": [[
{
"OrderID": 100,
"CustomerID": 2000,
"OrderDetail":
{
"ProductID":2000,
"UnitPrice":350
}
},
{
"OrderID": 200,
"CustomerID": 3000,
"OrderDetail":
{
"ProductID":2000,
"UnitPrice":350
}
}
]]
}'
Statement:
SELECT j2.*
FROM OPENJSON(#JSONData, '$.OrderHeader') j1
CROSS APPLY OPENJSON(j1.[value]) WITH (
OrderID INT '$.OrderID',
CustomerID INT '$.CustomerID',
ProductID INT '$.OrderDetail.ProductID',
UnitPrice INT' $.OrderDetail.UnitPrice'
) j2
Result:
OrderID CustomerID ProductID UnitPrice
100 2000 2000 350
200 3000 2000 350
Finally, if the $.OrderHeader JSON array always has a single item, the following statement is also an option:
SELECT *
FROM OPENJSON(#JSONData, '$.OrderHeader[0]') WITH (
OrderID INT '$.OrderID',
CustomerID INT '$.CustomerID',
ProductID INT '$.OrderDetail.ProductID',
UnitPrice INT' $.OrderDetail.UnitPrice'
)

Importing JSON data into SQL Server; losing decimal precision and scale

I'm importing an array of JSON documents, like the following:
{
"_id": {
"$oid": "6092e24247f3024d478902c2"
},
"startSearch": "2021-05-05T18:20:50.132Z",
"Inst_ID": 856,
"hostname": "localhost",
"searchpath": "/Account/Main/_LookupOptimize",
"Params": {
"AccountStatusDisplay": [
"Active",
"Out For Repo",
"Repossessed"
]
},
"endSearch": "2021-05-05T18:20:53.993Z",
"elapsedTime": 3.861,
"totalRecordsFound": 1988
}
I'm importing them into a local instance of SQL Server 2019 like so:
Declare #JSON nvarchar(max)
SELECT #JSON=BulkColumn
FROM OPENROWSET (BULK 'C:\temp\QueueSearch.JSON', SINGLE_CLOB) import
INSERT INTO myJSON
SELECT *
FROM OPENJSON (#JSON)
WITH
(
[_id] nvarchar(max),
[startSearch] datetime,
[Inst_ID] int,
[hostname] nvarchar(80),
[searchpath] nvarchar(50),
[Params] nvarchar(max),
[endSearch] datetime,
[elapsedTime] NUMERIC(10,4),
[totalRecordsFound] int
)
My problem is that the elapsedTime values are being rounded off to integers. (The _id values aren't getting imported either, but first things first.) I'm probably missing something really simple, but how can I preserve the accuracy of my elapsedTime values? Thanks in advance!
Verify the data type of elapsedTime is numeric(10,4) and explicitly map the columns in INSERT to make sure that you're inserting elapsedTime from the JSON into the elapsedTime column.
INSERT INTO myJSON([_id],startSearch,Inst_ID,hostname,searchpath,Params,endSearch,elapsedTime,totalRecordsFound)
SELECT *
FROM OPENJSON (#JSON)
WITH
(
[_id] nvarchar(max),
[startSearch] datetime,
[Inst_ID] int,
[hostname] nvarchar(80),
[searchpath] nvarchar(50),
[Params] nvarchar(max),
[endSearch] datetime,
[elapsedTime] NUMERIC(10,4),
[totalRecordsFound] int
)

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.