Parsing JSON from SQL Server - sql

I have a table with the following columns, one column being a JSON blob. I'm unclear how to parse the JSON blob as a series of columns alongside the other columns. I know there's something called OPENJSON, but not sure how to apply it to this case.
ID | ORGANIZATION | DEVICE_TIME | DEVICE | DATA
--------------------------------------------------------------------
011 015 2021-07-20 015 (JSON COLUMN)
012 016 2021-08-20 016 (JSON COLUMN)
The json string example is below, from the DATA column above
{
"device": {
"battery_level": 98,
"rssi": -105,
"boot_cnt": 5,
"apn": "teal",
"ip_addr": "10.176.30.171",
"fw_ver": "1.00",
"modem_fw": "mfw_nrf9160_1.3.0",
"imsi": "234500024531391",
"imei": "352656101040510",
"iccid": "8901990000000534985"
},
"data": {
"Temperature": 77.563942718505871,
"Humidity": 29.100597381591797,
"pressure": 28.883883226248145,
"air_quality": 37.067466735839844,
"SoilMoisture": 0.42462845010615713,
"Lat": 0,
"Long": 0,
"Alt": 0
}
}

openjson returns a table (possibly with many rows, although not for your sample).
To put something into a column you need a scalar. Try this example. Yes you need to explicitly list the columns out.
/* Create a sample table */
WITH MySampleTable
AS (
SELECT 1 as col1, 2 as col2, 'Hi There' as col3,
CAST('
{
"device": {
"battery_level": 98,
"rssi": -105,
"boot_cnt": 5,
"apn": "teal",
"ip_addr": "10.176.30.171",
"fw_ver": "1.00",
"modem_fw": "mfw_nrf9160_1.3.0",
"imsi": "234500024531391",
"imei": "352656101040510",
"iccid": "8901990000000534985"
},
"data": {
"Temperature": 77.563942718505871,
"Humidity": 29.100597381591797,
"pressure": 28.883883226248145,
"air_quality": 37.067466735839844,
"SoilMoisture": 0.42462845010615713,
"Lat": 0,
"Long": 0,
"Alt": 0
}
}
'
AS NVARCHAR(MAX)
) as myjsoncolumn
UNION ALL
SELECT 5,6,'Test','
{
"device": {
"battery_level": 2,
"rssi": -105,
"boot_cnt": 5,
"apn": "teal"
},
"data": {
"Humidity": 29.100597381591797,
"pressure": 28.883883226248145
}
}
'
)
SELECT *,
JSON_VALUE(myjsoncolumn,'$.device.battery_level') as battery_level,
JSON_VALUE(myjsoncolumn,'$.data.Temperature') as Temp
FROM MySampleTable

The statement usually depends on the structure of the parsed JSON data. In this case, a possible option is OPENJSON() with explicit schema (the WITH clause with columns definitions), using the appropriate data types and path expressions:
JSON and table:
DECLARE #json varchar(max) = '{
"device": {
"battery_level": 98,
"rssi": -105,
"boot_cnt": 5,
"apn": "teal",
"ip_addr": "10.176.30.171",
"fw_ver": "1.00",
"modem_fw": "mfw_nrf9160_1.3.0",
"imsi": "234500024531391",
"imei": "352656101040510",
"iccid": "8901990000000534985"
},
"data": {
"Temperature": 77.563942718505871,
"Humidity": 29.100597381591797,
"pressure": 28.883883226248145,
"air_quality": 37.067466735839844,
"SoilMoisture": 0.42462845010615713,
"Lat": 0,
"Long": 0,
"Alt": 0
}
}'
SELECT *
INTO Data
FROM (VALUES
('011', '015', '2021-07-20', '015', #json),
('012', '016', '2021-08-20', '016', #json)
) v (ID, ORGANIZATION, DEVICE_TIME, DEVICE, DATA)
Statement:
SELECT d.*, j.*
FROM Data d
OUTER APPLY OPENJSON(d.DATA) WITH (
batery_level int '$.device.battery_level',
rssi int '$.device.rssi',
boot_cnt int '$.device.boot_cnt',
apn varchar(10) '$.device.boot_apn',
ip_addr varchar(19) '$.device.ip_addr',
fw_ver varchar(5) '$.device.fw_ver',
modem_fw varchar(59) '$.device.modem_fw',
imsi varchar(15) '$.device.imsi',
imei varchar(15) '$.device.imei',
iccid varchar(50) '$.device.iccid',
Temperature numeric(20, 15) '$.data.Temperature',
Humidity numeric(22, 18) '$.data.Humidity',
pressure numeric(22, 18) '$.data.pressure',
air_quality numeric(22, 18) '$.data.air_quality',
SoilMoisture numeric(22, 18) '$.data.SoilMoisture',
Lat numeric(9, 6) '$.data.Lat',
Long numeric(9, 6) '$.data.Long',
Alt numeric(9, 6) '$.data.Alt'
) j

Related

SQL Server JSON output of nested tables

I have the following tables:
create table students
(
id int,
name varchar(10)
)
create table subjects
(
subjectId int,
studentId int,
subject varchar(12)
)
create table marks
(
studentId int,
subjectId int,
marks int
)
create table sports
(
sportId int,
studentId int,
name varchar(12)
)
with the following data:
insert into students values(1, 'Rusty');
insert into subjects values(1, 1, 'math')
insert into subjects values(2, 1, 'science')
insert into marks values(1,1,50)
insert into marks values(1,2,60)
insert into sports values(1, 1, 'soccer')
insert into sports values(2, 1, 'baseball')
I want to write a query in SQL Server to get the following output:
studentId = 1
{
"id": 1,
"name": "Rusty",
"subjects" : [
{
"name": "math",
"marks": 50
},
{
"name": "science",
"marks": 60
}
],
"sports": [
{
"name": "soccer"
},
{
"name": "baseball"
}
]
}
I tried the following query
select *
from students s
join subjects su on (s.id = su.studentId)
join sports sp on (s.id = sp.studentId)
where s.id = 1
for json auto
and here is the output:
[
{
"id": 1,
"name": "Rusty",
"su": [
{
"subjectId": 1,
"studentId": 1,
"subject": "math",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "soccer"
}
]
},
{
"subjectId": 1,
"studentId": 1,
"subject": "science",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "soccer"
}
]
},
{
"subjectId": 1,
"studentId": 1,
"subject": "math",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "baseball"
}
]
},
{
"subjectId": 1,
"studentId": 1,
"subject": "science",
"sp": [
{
"sportId": 1,
"studentId": 1,
"name": "baseball"
}
]
}
]
}
]
For your desired output you can use correlated subqueries for sports and subjects that generate their own JSON, using FOR JSON PATH, and include that information as an array of nested objects in your main JSON output by way of JSON_QUERY (Transact-SQL), e.g.:
/*
* Data setup...
*/
create table students (
id int,
name varchar(10)
);
create table subjects (
subjectId int,
studentId int,
subject varchar(12)
);
create table marks (
studentId int,
subjectId int,
marks int
);
create table sports (
sportId int,
studentId int,
name varchar(12)
);
insert into students (id, name) values
(1, 'Rusty');
insert into subjects (subjectId, studentId, subject) values
(1, 1, 'math'),
(2, 1, 'science');
insert into marks (studentId, subjectId, marks) values
(1,1,50),
(1,2,60);
insert into sports (sportId, studentId, name) values
(1, 1, 'soccer'),
(2, 1, 'baseball');
/*
* Example query...
*/
select
students.id,
students.name,
json_query(( --<<-- doubled brakcets
select
subjects.subject,
marks.marks
from subjects
join marks
on marks.subjectId = subjects.subjectId
and marks.studentId = subjects.studentId
where subjects.studentId = students.id
for json path
)) as [subjects],
json_query(( --<<-- doubled brackets
select
sports.name
from sports
where sports.studentId = students.id
for json path
)) as [sports]
from students
where students.id = 1
for json path, without_array_wrapper;
Which yields the JSON output:
{
"id": 1,
"name": "Rusty",
"subjects": [
{
"subject": "math",
"marks": 50
},
{
"subject": "science",
"marks": 60
}
],
"sports": [
{
"name": "soccer"
},
{
"name": "baseball"
}
]
}

insert json to sql table getting null

I have the following json , when i am trying to extract it to sql i get empty.
I need CusP.id, CusP.custldId ,cusfield.id, value
DECLARE #json NVARCHAR(MAX);
SELECT #json = '{
"includ": {
"cusP": {
"542310": {
"id": 542310,
"custldId": 155,
"cusfield": {
"id": 155,
"type": "custfi"
},
"projectId": 17435,
"project": {
"id": 17435,
"type": "projects"
},
"value": "META DATA",
"createdAt": "2022-01-16T05:11:20Z",
"createdBy": 222222
},
"21000": {
"id": 21000,
"custldId": 426,
"cusfield": {
"id": 426,
"type": "custfi"
},
"projectId": 786044,
"project": {
"id": 786044,
"type": "projects"
},
"value": "delta55",
"createdAt": "2022-01-17T10:03:07Z",
"createdBy": 333333
}
}
}
}'
This is what i am trying:
SELECT
D.cusPid,
d.[value],
c.cusfieldid,
cd.projectId
FROM OPENJSON(#json, '$.includ.cusP')
WITH (
cusPid NVARCHAR(max) '$.id',
[value] NVARCHAR(max) '$.value'
) D
CROSS APPLY OPENJSON(#json, '$.includ.cusP.custfi')
WITH (
cusfieldid VARCHAR(100) '$.id'
) C
CROSS APPLY OPENJSON(#json, '$.includ.cusP.project')
WITH (
projectId VARCHAR(100) '$.id'
) Cd;
that is the result i expect
cusPid
value
cusfieldid
projectId
542310
META DATA
155
17435
21000
delta55
426
786044
The problem is that the ID is also itself used as the key for a sub-property and OPENJSON does not allow variable paths (beyond arrays), so you need an extra level:
SELECT P.id AS cuspID, P.[value], P.cusfieldid, [projectId]
FROM OPENJSON(#json, '$.includ.cusP') J
CROSS APPLY OPENJSON(J.[value]) WITH (
id INT,
[value] NVARCHAR(MAX),
[projectId] INT,
cusfieldid INT '$.cusfield.id'
) P

Reading from JSON column with dynamic element name

I have a table that contains json data that I need to unpick and store in a relational DB. I am using an Oracle DB 19.0 and have created the following table:
CREATE TABLE J_PAGE
(
PK_PAGE_ID NUMBER ( 10 , 0 )
, FK_RESP_ID NUMBER ( 10 , 0 ) DEFAULT -1
, CL_PAGE_ID NUMBER ( 10 , 0 ) DEFAULT -1
, CL_RESP CLOB CONSTRAINT CK_PAGE_01 CHECK ( CL_RESP IS JSON )
, CL_DT DATE DEFAULT SYSDATE
) ;
The column CL_RESP is a clob value that returns the response from a call to UTL_HTTP.GET_RESPONSE.
{
"count": 2,
"results": [
{
"key": "stories",
"id": "10000"
},
{
"key": "stories",
"id": "10001"
}
],
"stories": {
"10000": {
"title": "story1",
"description": null,
"start_date": "2020-04-01",
"id": "10001"
},
"10001": {
"title": "story2",
"description": null,
"start_date": null,
"id": "10001"
}
},
"meta": {
"count": 2,
"page_count": 1,
"page_number": 1,
"page_size": 20
}
}
I need to extract the contents of the "stories" element but I'm getting stuck where the next element doesn't have a static name.
I know I can do the following to return 1 row using story 10001...
SELECT
page.CL_RESP.stories."10001".title[*] CL_TITLE
FROM
J_PAGE page ;
But I need all stories so perhaps a wildcard in place of 10001?
SELECT
page.CL_RESP.stories.*.title[*] CL_TITLE
FROM
J_PAGE page ;
Can anyone help?
Solved it.
SELECT
t.*
FROM
J_PAGE o,
JSON_TABLE ( o.CL_RESP , '$.stories.*'
COLUMNS (
CL_TITLE VARCHAR2 PATH '$.title'
, CL_DESC VARCHAR2 PATH '$.description'
, CL_START_DT TIMESTAMP PATH '$.start_date'
, CL_ID NUMBER PATH '$.id'
)
) t ;

Use SQL Server's FOR JSON function with both Numeric and JSON inputs

I'm struggling to combine the contents of a single table in SQL Server that contains both numeric data and JSON-formatted strings into a consolidated JSON output using SQL Server's "FOR JSON" function.
I'm running into issues with the escape characters mainly, doesn't appear to be an easy approach to combining JSON and non-JSON data into a JSON-formatted SQL Server Query output. Can anyone point me to an example of a successful query that's done this?
Sample Data:
CREATE TABLE settings (
Id INT PRIMARY KEY NOT NULL,
FileName nvarchar(max) NOT NULL,
FilePath nvarchar(max) NOT NULL,
Date datetime NOT NULL,
Json nvarchar(max)
);
INSERT INTO settings
(
Id,
FileName,
FilePath,
Date,
Json
)
VALUES
(
1,
'contents.json',
'folder1/folder2/contents.json',
'2000-01-01T00:00:00.000',
'{
"A": 10,
"B": 20,
"C": {
"setting1": 30,
"setting2": 40,
"setting3": 50
},
"D": {
"setting1": 30,
"setting2": 40,
"setting3": 50
},
"E": true,
"F": false
"G": "John Doe"
}'
);
Sample Query:
SELECT
FileName
, FilePath
, Id
, Date
, Json
FROM settings
FOR JSON AUTO;
Expected Output:
[
{
"FileName": "contents.json",
"FilePath": "folder1/folder2/contents.json",
"Id": 1,
"Date": "2000-01-01T00:00:00.000",
"Json": [
{
"A": 10,
"B": 20,
"C": {
"setting1": 30,
"setting2": 40,
"setting3": 50
},
"D": {
"setting1": 30,
"setting2": 40,
"setting3": 50
},
"E": true,
"F": false
"G": "John Doe"
}
]
}
]
Your example settings.Json is invalid:
"F": false
Should be:
"F": false,
Once you fix that issue you can change your query to nest Json's JSON like this:
SELECT
FileName
, FilePath
, Id
, Date
, [Json] = Json_Query(concat('[', Json, ']'))
FROM settings
FOR JSON AUTO;
Which yields the result:
[
{
"FileName": "contents.json",
"FilePath": "folder1\/folder2\/contents.json",
"Id": 1,
"Date": "2000-01-01T00:00:00",
"Json": [
{
"A": 10,
"B": 20,
"C": {
"setting1": 30,
"setting2": 40,
"setting3": 50
},
"D": {
"setting1": 30,
"setting2": 40,
"setting3": 50
},
"E": true,
"F": false,
"G": "John Doe"
}
]
}
]
Note that SQL Server has escaped / characters in FilePath using \/ as per the JSON specification.

Converting JSON file data values to SQL tables rows and columns

I have the below code in JSON file :
{
"took": 196,
"timed_out": false,
"_shards": {
"total": 15,
"successful": 15,
"failed": 0
},
"hits": {
"total": 165,
"max_score": null,
"hits": [
{
"_index": "logstash-2018.11.22",
"_type": "nagios_core",
"_id": "AWc6C_EtHRYvW4hmI7sl",
"_score": null,
"_source": {
"message": "EXTERNAL COMMAND: ACKNOWLEDGE_SVC_PROBLEM;DE-Hoeheinoed-VOC1-SRV;ntp_timesync;2;0;0;Jaizel Jem Perdon;SN 307185410",
"#version": "1",
"#timestamp": "2018-11-22T06: 12: 00.307Z",
"host": "172.26.66.59",
"port": 44154,
"type": "nagios_core",
"epoch_timestamp": "1542867118",
"nagios_severity_label": "EXTERNAL COMMAND",
"nagios_external_command": "ACKNOWLEDGE_SVC_PROBLEM",
"nagios_host": "DE-Hoeheinoed-VOC1-SRV",
"nagios_service": "ntp_timesync",
"nagios_sticky": "2",
"nagios_notify": "0",
"nagios_persistent": "0",
"nagios_author": "Jaizel Jem Perdon",
"nagios_comment": "SN 307185410",
"utc_timestamp": "2018-11-22T06: 11: 58.000Z"
},
"sort": [
1542867120307
]
}
]
}
}
And i have the below code in SQL: However, I am getting null values in my result. As am new to JSON, am not able to find out the path of the JSON data values
Drop table if exists #Temp1
Declare #JSON nvarchar(max)
SELECT #JSON = BulkColumn
FROM OPENROWSET (BULK '\\DKRDSDFSROOT10\Data\_Temp\MEIPE\ITE1452552_02test.json', SINGLE_CLOB) as j
select #json as details
If (ISJSON(#json) = 1)
BEGIN
PRINT 'JOSN File is valid';
select * into #Temp1
from OPENJSON(#JSON, '$.hits')
WITH
(
[nagios_author] nvarchar(100) '$.hits.hits._source.nagios_author',
[nagios_comment] nvarchar(100) '$.hits.hits._source.nagios_comment'
)
END
ELse
Begin
PRINT 'JOSN File is invalid';
END
select * from #Temp1
Can someone please help me ?
You are using the second hits array in your json as an object, but its an array try to change your query as follows:
select * into #Temp1
from OPENJSON(#JSON, '$.hits')
WITH
(
[nagios_author] nvarchar(100) '$.hits.hits[0]._source.nagios_author',
[nagios_comment] nvarchar(100) '$.hits.hits[0]._source.nagios_comment'
)
I tried using below code and it worked
WITH
(
[nagios_author] nvarchar(100) '$.hits[0]._source.nagios_author',
[nagios_comment] nvarchar(100) '$.hits[0]._source.nagios_comment'
)