Parse JSON file - Improve query in SQL Server - sql-server

From this JSON File (just an example) I need to reach this final result
{
"Id": "101",
"name": "C01",
"testparameters": {
"room": [
{
"Floor": "First_Floor",
"Rooms": ["Room1", "Room2", "Room3"]
},
{
"Floor": "Second_Floor",
"Rooms": ["Room1", "Room2", "Room3"]
}
],
"area": [
{
"Name": "Area1",
"Subarea": ["Subarea1", "Subarea2", "Subarea3"]
},
{
"Name": "Area2",
"Subarea": ["Subarea4", "Subarea5"]
}
],
"requirements": [{
"condition": "",
"type": "type1",
"field1": "",
"field2": "aaaaa",
"operator": "",
"value2": ""
},
{
"condition": "AND",
"type": "type2",
"field1": "",
"field2": "numPersons",
"operator": ">",
"value2": "20"
},
{
"condition": "OR",
"type": "type2",
"field1": "",
"field2": "specification",
"operator": "=",
"value2": "wifi"
}
]
}
}
'
In one register I need to have all the information that is requested.
This is the first time that I need to parse a JSON file. After asking (a lot) I manage to reach the expected result by doing this:
Parsing JSON Example
However, I had to open the JSON file several times, and process each section apart. I'm wondering, how can I improve the code by reducing the number of times that I need to use the OPENJSON function, and in particular, how to rewrite the code snippet that handle the requirements section.

I must say, your desired result looks pretty de-normalized, you may want to rethink it.
Be that as it may, you can combine these quite easily, by using nested subqueries
SELECT
ID = JSON_VALUE(j.json, '$.Id'),
name = JSON_VALUE(j.json, '$.name'),
area = (
SELECT STRING_AGG(concat(d.a , ':', b.value),' - ')
from openjson(j.json, '$.testparameters.area')
with
(
a nvarchar(250) '$.Name',
s nvarchar(max) '$.Subarea' as json
) as d
cross apply openjson(d.s) as b
),
room = (
SELECT STRING_AGG(concat(c.f, ':', d.value), ' - ')
from openjson(j.json, '$.testparameters.room')
with(
f nvarchar(50) '$.Floor',
r nvarchar(Max) '$.Rooms' as json
) as c
cross apply openjson(c.r) as d
),
requirements = (
SELECT IIF(
SUBSTRING(requirements,1,3) = 'AND' or SUBSTRING(requirements,1,3) = 'OR',
SUBSTRING(requirements,5,LEN(requirements)),
requirements
)
from
(
select
STRING_AGG(CONCAT_WS(' ',
a.condition,
a.field2,
operator,
IIF (ISNUMERIC(a.value2) = 1,
a.value2,
CONCAT('''',a.value2,'''')
)
),
' ') as requirements
from openjson(j.json, '$.testparameters.requirements' )
with
(
condition nvarchar(255) '$.condition',
type nvarchar(255) '$.type',
field2 nvarchar(255) '$.field2',
operator nvarchar(255) '$.operator',
value2 nvarchar(255) '$.value2'
) a
where a.type = 'type2'
) a
)
FROM (VALUES(#json)) AS j(json) -- or you can reference a table

Related

SQL Server 2019 - Build JSON with spread operator

I'm required to supply a json object like this:
[
{
id: '59E59BC82852A1A5881C082D5FFCAC10',
user: {
...users[1],
last_message: "16-06-2022",
topic: "Shipment"
},
unread: 2,
},
{
id: '521A754B2BD028B13950CB08CDA49075',
user: {
...users[2],
last_message: "15-06-2022",
topic: "Settings"
},
unread: 0,
}
]
it is not difficult for me to build a json like this:
(with this fiddle https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=bf62626de20d3ca7191aa9c1ef0cd39b)
[
{
"id": "59E59BC82852A1A5881C082D5FFCAC10",
"user": {
"id": 1,
"last_message": "16-06-2022",
"topic": "Shipment"
},
"unread": 2
},
{
"id": "521A754B2BD028B13950CB08CDA49075",
"user": {
"id": 2,
"last_message": "15-06-2022",
"topic": "Settings"
},
"unread": 1
},
{
"id": "898BB874D0CBBB1EFBBE56D2626DC847",
"user": {
"id": 3,
"last_message": "18-06-2022",
"topic": "Account"
},
"unread": 1
}
]
but I have no idea how to put the ...users[1], instead of "id": 1 into user node:
is there a way?
This is not actually valid JSON, but you can create it yourself using STRING_AGG and CONCAT
SELECT
'[' + STRING_AGG(u.spread, ',') + ']'
FROM (
SELECT
spread = CONCAT(
'{id:''',
u.userId,
''',user:{...users[',
ROW_NUMBER() OVER (ORDER BY u.id),
'],last_message: "',
t.created_at,
'",topic:"',
t.topic,
'"},unread:',
(SELECT COUNT(*) FROM #tickets t3 WHERE t3.userId = u.userId AND t3.read_at IS NULL),
'}'
)
FROM #Users u
CROSS APPLY (
SELECT top 1
t.ticketId,
t.created_at,
t.topic
FROM #Tickets t
WHERE t.userId = u.userId
ORDER BY
t.created_at DESC
) t
) u
Note that you may need to escape values, but I don't know how this not-JSON works so couldn't say.
db<>fiddle

TSQL - Pivot not returning all rows

I have the following JSON array data set that needs to be parsed into 2 table rows:
[
{ "eid": "ABCDGD",
"name": "Carol E",
"email": "carole#gmail.com",
"role": "Recruiter"
},
{ "eid": "HDHDK",
"name": "Mark H",
"email": "markh#gmail.com",
"role": "Manager"
}
]
I need the code below to return both sets of employee information but it only returns one. How do I achieve this?
select p.* from
(SELECT j2.[key] as _keys, j2.Value as _vals
FROM OPENJSON(#c) j1
CROSS APPLY OPENJSON(j1.Value) j2
) as pds
PIVOT
(
max(pds._vals)
FOR pds._keys IN([eid], [name], [email], [role])
) AS p
SQLfiddle - http://sqlfiddle.com/#!18/9eecb/54970
No need to pivot, just specify your json columns and will give your desired results.
SELECT *
FROM OPENJSON(#c) WITH (
eid varchar(200) '$.eid',
name varchar(200) '$.name',
email varchar(200) '$.email',
role varchar(200) '$.role'
) j1
JSON is already maintaining a table kind structure in it and can be directly converted into table by using OPENJSON.
As the syntax of OPENJSON on MSDN website.
OPENJSON( jsonExpression [ , path ] ) [ <with_clause> ]
<with_clause> ::= WITH ( { colName type [ column_path ] [ AS JSON ] } [ ,...n ] )
Here just need to pass your column name as maintained in JSON and will convert your JSON into SQL Table.
You may find more details on this link.
For your above query you may try this.
DECLARE #json NVARCHAR(MAX)
SET #json =
N'[
{ "eid": "ABCDGD",
"name": "Carol E",
"email": "carole#gmail.com",
"role": "Recruiter"
},
{ "eid": "HDHDK",
"name": "Mark H",
"email": "markh#gmail.com",
"role": "Manager"
}
]'
SELECT *
FROM OPENJSON(#json)
WITH (
eid nvarchar(50) '$.eid',
name nvarchar(50) '$.name',
email nvarchar(50) '$.email',
role nvarchar(50) '$.role',
)

Postgresql get elements of a JSON array

Let's say that we have the following JSON in Postgresql:
{ "name": "John", "items": [ { "item_name": "lettuce", "price": 2.65, "units": "no" }, { "item_name": "ketchup", "price": 1.51, "units": "litres" } ] }
The JSONs are stored in the following table:
create table testy_response_p (
ID serial NOT NULL PRIMARY KEY,
content_json json NOT NULL
)
insert into testy_response_p (content_json) values (
'{ "name": "John", "items": [ { "item_name": "lettuce", "price": 2.65, "units": "no" }, { "item_name": "ketchup", "price": 1.51, "units": "litres" } ] }'
)
Since the following can return either JSON or text (with -> and ->> respectively select content_json ->> 'items' from testy_response_p) I want to use a subquery in order to get elements of the array under items:
select *
from json_array_elements(
select content_json ->> 'items' from testy_response_p
)
All I get is an error but I don't know what I'm doing wrong. The output of the subquery is text. The final output is:
{ "item_name": "lettuce", "price": 2.65, "units": "no" }
{ "item_name": "ketchup", "price": 1.51, "units": "litres" }
You need to join to the function's result. You can't use the ->> operator because that returns text, not json and json_array_elements() only works with a JSON value for its input.
select p.id, e.*
from testy_response_p p
cross join lateral json_array_elements(p.content_json -> 'items') as e;
Online example: https://rextester.com/MFGEA29396

SQL Server JSON Array

I have some JSON that I would like to parse in SQL Server 2016. There is a hierarchy structure with arrays. I would like to write a query that parses the whole hierarchy more efficiently, I have challenges trying to access embedded arrays especially anything under "DealerPrefLocation", while I have no problems accessing anything under "DealerInformation", below is my sample JSON:
{
"DealerInformation": {
"Altername": [
{
"firstName": "two",
"lastName": "one",
"middleName": null,
"otherNameExplanation": "change"
}
],
"DealerType": {
"id": "87ab-098ng-2345li",
"name": "DMD"
},
"firstName": "PK",
"middleName": null,
"lastName": "KPK",
"primaryDealerState": "AP",
"otherDealerState": [
"AP",
"MP"]
},
"DealerPrefLocation": [
{
"PrefLocation": [
{
"address": {
"address1": "fort warangal",
"address2": "east",
"addressStandardizationSource": null,
"city": "warangal",
"country": "India"
},
"apptPhoneNumber": "989898989898",
"createdAt": null,
"phoneNumber": "989898989898"
}
],
"NonPrefLocation": [
{
"address": {
"address1": "fort Junction",
"address2": null,
"addressStandardizationSource": null
},
"createdAt": null,
"ServiceName": "H1",
"ServiceId": [
{
"ServiceGroupName": null,
"Type": "GROUP",
"ServiceNumber": "9999999"
}
]
}
],
"Inserted": null,
"Updated": null }
]
}
I did figure out how to query "DealerInformation" and arrays within it such as "AlterName" and "OtherDealerState", however I have challenges querying arrays under "DealerInformation"-->"PrefLocation"-->Address.
Please find my current query and output:
select
ID,
JSON_VALUE(VALUE_ID,'$.DealerInformation.firstName'),
JSON_VALUE(VALUE_ID,'$.DealerInformation.primaryDealerState'),
JSON_VALUE(A.VALUE,'$.firstName'),
JSON_VALUE(C.VALUE,'$.PrefLocation.address.address1')
from
Test_JSON_File
cross apply
openjson(Test_JSON_File.value_id,'$.DealerInformation.Altername')A
cross apply
openjson(Test_JSON_File.Test_JSON_CAQH.value_id,'$.DealerPrefLocation')C
The last column I selected is from "DealerPrefLocation" but I get only nulls, can someone help what am I missing in the SQL or what do I need to add?
Sorry, this answer is rather late...
I think, the most important information for you is the AS JSON within the WITH-clause. See how I use it:
DECLARE #json NVARCHAR(MAX) =
N' {
"DealerInformation": {
"Altername": [
{
"firstName": "two",
"lastName": "one",
"middleName": null,
"otherNameExplanation": "change"
}
],
"DealerType": {
"id": "87ab-098ng-2345li",
"name": "DMD"
},
"firstName": "PK",
"middleName": null,
"lastName": "KPK",
"primaryDealerState": "AP",
"otherDealerState": [
"AP",
"MP"]
},
"DealerPrefLocation": [
{
"PrefLocation": [
{
"address": {
"address1": "fort warangal",
"address2": "east",
"addressStandardizationSource": null,
"city": "warangal",
"country": "India"
},
"apptPhoneNumber": "989898989898",
"createdAt": null,
"phoneNumber": "989898989898"
}
],
"NonPrefLocation": [
{
"address": {
"address1": "fort Junction",
"address2": null,
"addressStandardizationSource": null
},
"createdAt": null,
"ServiceName": "H1",
"ServiceId": [
{
"ServiceGroupName": null,
"Type": "GROUP",
"ServiceNumber": "9999999"
}
]
}
],
"Inserted": null,
"Updated": null }
]
}';
--I'll pick at least one element out of each region. This should point you the way:
SELECT B.firstName
,B.middleName
,B.lastName
,JSON_VALUE(B.DealerType,'$.id') AS DealerTypeId
,B.PrimaryDealerState
,B.otherDealerState --You can dive deeper to parse that array
,JSON_VALUE(B.Altername,'$[0].firstName') AS Alter_firstName --there might be more...
,JSON_VALUE(C.PrefLocation,'$[0].address.address1') AS pref_address --there might be more...
,JSON_VALUE(C.PrefLocation,'$[0].apptPhoneNumber') AS pref_apptPhoneNumber
,JSON_VALUE(C.NonPrefLocation,'$[0].address.address1') AS nonpref_address --there might be more...
,JSON_VALUE(C.NonPrefLocation,'$[0].ServiceName') AS nonpref_ServiceName
FROM OPENJSON(#json)
WITH(DealerInformation NVARCHAR(MAX) AS JSON
,DealerPrefLocation NVARCHAR(MAX) AS JSON) A
OUTER APPLY OPENJSON(A.DealerInformation)
WITH(Altername NVARCHAR(MAX) AS JSON
,DealerType NVARCHAR(MAX) AS JSON
,firstName NVARCHAR(MAX)
,DealerType NVARCHAR(MAX) AS JSON
,middleName NVARCHAR(MAX)
,lastName NVARCHAR(MAX)
,primaryDealerState NVARCHAR(MAX)
,otherDealerState NVARCHAR(MAX) AS JSON) B
OUTER APPLY OPENJSON(A.DealerPrefLocation)
WITH(PrefLocation NVARCHAR(MAX) AS JSON
,NonPrefLocation NVARCHAR(MAX) AS JSON) C
UPDATE Select out of a table
Try this
SELECT B.firstName
,B.middleName
,B.lastName
,JSON_VALUE(B.DealerType,'$.id') AS DealerTypeId
,B.PrimaryDealerState
,B.otherDealerState --You can dive deeper to parse that array
,JSON_VALUE(B.Altername,'$[0].firstName') AS Alter_firstName --there might be more...
,JSON_VALUE(C.PrefLocation,'$[0].address.address1') AS pref_address --there might be more...
,JSON_VALUE(C.PrefLocation,'$[0].apptPhoneNumber') AS pref_apptPhoneNumber
,JSON_VALUE(C.NonPrefLocation,'$[0].address.address1') AS nonpref_address --there might be more...
,JSON_VALUE(C.NonPrefLocation,'$[0].ServiceName') AS nonpref_ServiceName
FROM Test_JSON_File
CROSS APPLY OPENJSON(value_id)
WITH(DealerInformation NVARCHAR(MAX) AS JSON
,DealerPrefLocation NVARCHAR(MAX) AS JSON) A
OUTER APPLY OPENJSON(A.DealerInformation)
WITH(Altername NVARCHAR(MAX) AS JSON
,DealerType NVARCHAR(MAX) AS JSON
,firstName NVARCHAR(MAX)
,DealerType NVARCHAR(MAX) AS JSON
,middleName NVARCHAR(MAX)
,lastName NVARCHAR(MAX)
,primaryDealerState NVARCHAR(MAX)
,otherDealerState NVARCHAR(MAX) AS JSON) B
OUTER APPLY OPENJSON(A.DealerPrefLocation)
WITH(PrefLocation NVARCHAR(MAX) AS JSON
,NonPrefLocation NVARCHAR(MAX) AS JSON) C;

Nest Json Array merge

I have a column saved json data in my table:
declare #json nvarchar(max)
set #json = N'
{
"Companies": [
{
"CompanyId": "A",
"Employee": null
},
{
"CompanyId": "B",
"Employee": [
{
"EmployeePictureId": null,
"Name": "Employee1"
},
{
"EmployeePictureId": "PictureId2",
"Name": "Employee2"
}
]
},
{
"CompanyId": "C",
"Employee": [
{
"EmployeePictureId": null,
"Name": "Employee3"
},
{
"EmployeePictureId": null,
"Name": "Employee4"
}
]
}
]
}
'
Is it posible to get the result like:
{
"EmployeePictureIds": ["PictureId2"]
}
using the Json_Query, Json_Value, OPENJSON...
Only get EmployeePictureId and skip empty(null) data
By the way, the count of elements in array are not sure.
In SQL Server 2017 you can use the following query:
select json_query(QUOTENAME(STRING_AGG('"' + STRING_ESCAPE( A.EmployeePictureId , 'json')
+ '"', char(44)))) as [EmployeePictureIds]
FROM OPENJSON(#json, '$.Companies')
WITH
(
CompanyId NVARCHAR(MAX),
Employee NVARCHAR(MAX) as json
) as B
cross apply openjson (B.Employee)
with
(
EmployeePictureId VARCHAR(50),
[Name] VARCHAR(50)
) as A
where A.EmployeePictureId is not null
for json path , WITHOUT_ARRAY_WRAPPER
Results for the JSON you provided:
Results adding another non null EmployeePictureId:

Resources