Dealing with nested JSON in SQL OPENJSON WITH command? - sql

I have JSON value that has some structure to it but I'm struggling to get to the 3rd level. I am using CROSS APPLY OPENJSON to get to the "Lines" data but I need to get the "Code" out of the TaxCode area... It seems to be it's own JSON array maybe?
Any help would be appreciated... This is what I have so far...
DECLARE #JSONText NVarChar(max) = '{
"UID": "845bc256-6027-4a89-8c05-35e4bb8e6aba",
"Number": "00013608",
"Lines": [{
"RowID": 1,
"Total": 20.0,
"TaxCode": "#{UID=f2cc83e5-0f7f-4831-9d88-dbe110e0683a; Code=S15}"
},{
"RowID": 2,
"Total": 55.49,
"TaxCode": "#{UID=a5cc34e5-0fr4-4325-9d67-bdh110e0683a; Code=S17}"
}]
}';
SELECT J.[UID],J.[Number],LI.*
FROM OPENJSON (#JSONText)
WITH (
[UID] nvarchar(512) '$."UID"',
[Number] nvarchar(50) '$."Number"',
[LineItems] NVarChar(max) '$."Lines"' AS JSON
) J
CROSS APPLY OPENJSON (J.[LineItems])
WITH (
[RowID] INT '$."RowID"',
[Total] Decimal(12,2) '$."Total"',
[TaxCode] NVarChar(512) '$."TaxCode"',
[TaxCodeTest] NVarChar(50) '$."TaxCode.Code"'
) LI;

As the TaxCode value being supplied wasn't a "true" JSON object I had to use some SQL to work with it as a string end get the required data. I ended up creating a function to do it using inspiration from this question...
A SQL Query to select a string between two known strings

Related

Reading JSON string and find the max value as integer

I have a JSON string as follows:
DECLARE #json nvarchar(max)
SET #json = '{"value": [
{
"AEDAT": "20211110"
},
{
"AEDAT": "20211110"
},
{
"AEDAT": "20211110"
},
{
"AEDAT": "20211112"
},
{
"AEDAT": "20211112"
},
{
"AEDAT": "20211112"
}
]}';
Now I want to read this JSON in SQL Server using OPENJSON() and find the MAX value for each AEDAT. For this, I am using the following query:
SELECT MAX(value)
FROM OPENJSON(#json, '$.value')
The above query is returning a row with key value pair as below:
{"AEDAT":"20211112"}
My objective is to get only 20211112 as integer.
How to achieve this?
If you want to get the max value as integer, you need to use OPENJSON() with explicit schema (the WITH clause with columns definitions). This schema depends on the structure of the parsed JSON (in your case it's a JSON array):
SELECT MAX(AEDAT) AS MaxAEDAT
FROM OPENJSON(#json, '$.value') WITH (
AEDAT int '$.AEDAT'
)
If the parsed values are dates, you may try a different statement:
SELECT MAX(TRY_CONVERT(date, AEDAT, 112)) AS MaxAEDAT
FROM OPENJSON(#json, '$.value') WITH (
AEDAT varchar(8) '$.AEDAT'
)
OPENJSON without explicit schema, gives you the value column which, in your example, will contain an object such as {"AEDAT": "20211110"} having type = 5. Use JSON_VALUE on that object:
select max(cast(json_value(j.value, '$.AEDAT') as int))
from openjson(#json, '$.value') as j

Create JSON string using variable

I am trying to get following JSON string:
[{
"Name": "John",
"AccountType": 1
},
{
"Name": "Steven",
"AccountType": 1
}
]
I know that AccountType will be always 1 and I have string variable in following format "John;Steven;Brian;Mike"
I was trying to build this JSON using XML PATH and splitToTable function but with no success. How can I achieve it?
Thanks in advance.
In SQL Server 2016 Microsoft added some useful JSON functions. So this can be achieved very easily:
DECLARE #Variable nvarchar(max) = 'John;Steven;Brian;Mike';
SELECT [value] as Name, 1 as AccountType FROM STRING_SPLIT(#Variable,';')
FOR JSON PATH
More info about this functions here - https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver15
You can use string_split() and for json:
declare #str nvarchar(max) = 'John;Steven;Brian;Mike';
select *
from (
select value as [Name], 1 as [AccountType]
from string_split(#str, ',')
) t
for json auto;

Retrieve values in a list in JSON using TSQL

I have this JSON and I want to get all the values in keyPhrases to be displayed as a column in TSQL query. Do you know how it can be done?
Desired Output:
ID
keyPhrases
778124772
banker, good position, friends family
JSON:
{"documents": [{"id": "778124772", "keyPhrases": ["banker", "good position", "friends", "family" ], "warnings": []}]}
Really appreciate your help.
The OPENJSON can help you, so the potential solution can be:
DECLARE #json NVARCHAR(MAX) = '{"documents": [{"id": "778124772", "keyPhrases": ["banker", "good position", "friends", "family" ], "warnings": []}]}';
SELECT
R.id as ID,
(SELECT STRING_AGG(value, ', ') FROM OPENJSON(R.kp)) as keyPhrases
FROM OPENJSON(#json, '$.documents')
WITH (id bigint, kp nvarchar(max) '$.keyPhrases' as JSON) R
Just to know, OPENJSON and STRING_AGG are features related to newer versions of SQL Server.
You can use OPENJSON() and STRING_AGG() as the following:
DECLARE #JSON NVARCHAR(MAX) = N'{"documents": [{"id": "778124772", "keyPhrases": ["banker", "good position", "friends", "family" ], "warnings": []}]}';
SELECT JSON_VALUE(T.Value, '$.id') AS ID,
STRING_AGG(TT.Value, ',') AS keyPhrases
FROM OPENJSON(#JSON, '$.documents') AS T
CROSS APPLY OPENJSON(Value, '$.keyPhrases') AS TT
GROUP BY JSON_VALUE(T.Value, '$.id');
Here is a db<>fiddle

DB2 LUW - JSON_Table reading json array data into tabular format

I am trying to convert JSON array data into tabular format using Json_Table function. I tried to run the below query but I am getting the following errors:
SQL‬‎/‪JSON‬‎ ‪scalar‬‎ ‪required‬‎.‪‬‎.‪‬‎ ‪SQLCODE‬‎=‪‬‎-‪16413‬‎,‪‬‎ ‪SQLSTATE‬‎=‪2203F‬‎,‪‬‎ ‪DRIVER‬‎=‪4‬‎.‪19‬‎.‪56).
The same query is working fine when the number of elements in Employees array is 1, but not otherwise.
SELECT E."id", E."name"
FROM JSON_TABLE
(
'{
"Employees": [
{
"id": 1,
"name": "Kathi"
},
{
"id": 2,
"name": "Pavan"
}
]
}', 'strict $' COLUMNS
(
"id" INTEGER PATH 'strict $.Employees[*].id'
, "name" VARCHAR(20) PATH 'strict $.Employees[*].name'
) ERROR ON ERROR
) AS E;
Just for the benefit of others who are looking for some examples, i found below link in internet, example 3 basically helps with my case. We need to use JSON_Table from SysTools schema.
https://www.worldofdb2.com/profiles/blogs/convert-json-data-to-relational-format
JSON_TABLE produces 1 row only per 1 json document.
You may use the following only:
"id" INTEGER PATH 'strict $.Employees[n].id'
, "name" VARCHAR(20) PATH 'strict $.Employees[n].name'
where n={0, 1} for an array of 2 elements as in your example.

Parsing full JSON into SQL

I have the following JSON (only a couple of nodes for display purposes):
[
{
"CareNotes": [
{
"CareNoteID": "34289e11-6433-4020-9734-224eb8caa11a",
"CareNoteExtendedID": "00000000-0000-0000-0000-000000000000",
"ADLName": "Mobility",
"FlagsText": "",
"Note": "Help with walking, used as four wheel walker, was content.",
"AnswerType": 1,
"Fragment": "Help with walking",
"RemedialText": null,
"Details": null,
"ServiceUserID": "bc300962-3653-491a-9ba9-afab10964af4",
"ServiceUser": "Betty Test",
"ServiceUserLastName": "Test",
"ServiceUserForeNames": "Betty",
"ServiceUserDateofBirth": "19/03/1901",
"ServiceUserLocation": 15,
"WorkerID": "53e6c7b9-2c80-451e-ba8c-abfb309380ac",
"Worker": "Beth Beth",
"VoidedByWorker": null,
"_supersedeStackID": null,
"SupersededByWorker": null,
"WorkerLastName": "Beth",
"DisplayOnShiftHandover": 0,
"WorkerInitials": "B.B.",
"SliderData": "Walk",
"SliderData2": "Not entered",
"SliderIcons": [
{
"IconID": 1093,
"CareNoteText": "was content"
},
{
"IconID": 1156,
"CareNoteText": "used as four wheel walker"
}
],
"DateDone": "2019-09-30T21:24:41.994+00:00",
"DateDoneSU": "2019-09-30T21:24:41.994+00:00",
"Duration": "9 minutes",
"DurationInt": 9,
"ActionIconID": 6001,
"mraCareOrder": 5000,
"wasPlanned": false,
"qrVerified": false,
"qrData": null,
"nfcVerified": null,
"inVerified": null,
"ViaMonitor": null
}
]
}
]
I am not particularly good at SQL and have been frantically relearning what I did at uni and an old job 13yrs ago, in order to complete a project that takes JSON data from APIs of a care management solution, into my Delphi application that then processes the data to work out this and that. The JSON format is different depending on the report, and in the case of the CareNotesReport, the above JSON is what is produced.
My Delphi app pulls this JSON verbatim and dumps it to a .json file, with an ADO query that then executes the following code (along with some other select queries that are irrelevant here):
use CMUtility;
DECLARE #JSON VARCHAR(MAX)
SELECT #JSON = BulkColumn
FROM OPENROWSET
(BULK 'C:\Users\User\Documents\Embarcadero\Studio\Projects\CMU\Win32\Debug\carenotesreport.json', SINGLE_CLOB)
AS j
drop table if exists jsoncarenotes
select * into JSONCareNotes
from OPENJSON(#JSON,'$.CareNotes')
with (
DateDone nvarchar(10) '$.DateDone',
ServiceUser nvarchar(100) '$.ServiceUser',
ServiceUserLastName nvarchar(50) '$.ServiceUserLastName',
SUDOB nvarchar(15) '$.ServiceUserDateofBirth',
Note nvarchar(255) '$.Note',
ADLName nvarchar(200) '$.ADLName',
FlagsText nvarchar(255) '$.FlagsText',
Fragment nvarchar(255) '$.Fragment',
RemedialText nvarchar(255) '$.RemedialText',
Worker nvarchar(30) '$.Worker',
ServiceUserID nvarchar(100) '$.ServiceUserID',
WorkerID nvarchar(100) '$.WorkerID',
CareNoteID nvarchar(255) '$.CareNoteID',
SID1 nvarchar(255) '$.SliderIcons[0].IconID',
SText1 nvarchar(255) '$.SliderIcons[0].CareNoteText',
SID2 nvarchar(255) '$.SliderIcons[1].IconID',
SText2 nvarchar(255) '$.SliderIcons[1].CareNoteText',
SID3 nvarchar(255) '$.SliderIcons[2].IconID',
SText3 nvarchar(255) '$.SliderIcons[2].CareNoteText',
SID4 nvarchar(255) '$.SliderIcons[3].IconID',
SText4 nvarchar(255) '$.SliderIcons[3].CareNoteText',
SID5 nvarchar(255) '$.SliderIcons[4].IconID',
SText5 nvarchar(255) '$.SliderIcons[4].CareNoteText'
)
as CareNotes
I have a couple of issues. With the above code, I've had to strip the first [ and ] from the JSON file to get it to work, but due to the size of some returns I've had to change the use of a memorystream to a filestream within Delphi. This has created the problem in that although I can trim the last ] of the file, I am so far unable to find a reliable (and easy) method for trimming the first [. Therefore, I'm forced to conclude that it is my SQL code that is the weak link, and that it needs to be able to process the JSON with these two characters included.
Please can someone show me where I'm going wrong. I'm aware that the original JSON appears to be array, object, array, object, but I don't know where to go from that. Also, I've fudged the ability to read SliderIcons as I know there are a maximum of 5 objects of that array, but would prefer a more dynamic solution if possible.
Any assistance given in terms of either the way to remove that first [ in Delphi, or better SQL to handle the original JSON would be greatly appreciated.
Regards
Ant
You can parse this JSON input using SQL Server capabilities. If your JSON input has this fixed format (an array with one item and nested JSON arrays), you need an additional APPLY operator with OPENJSON() call to parse the nested JSON array. Note, that when the referenced property contains an inner JSON object or array you need to use the AS JSON option in the column definition.
JSON:
DECLARE #json nvarchar(max)
--SELECT #json = BulkColumn
--FROM OPENROWSET (BULK 'C:\Users\User\Documents\Embarcadero\Studio\Projects\CMU\Win32\Debug\carenotesreport.json', SINGLE_CLOB) AS j
SELECT #json = N'[
{
"CareNotes":[
{
"CareNoteID":"34289e11-6433-4020-9734-224eb8caa11a",
"CareNoteExtendedID":"00000000-0000-0000-0000-000000000000",
"ADLName":"Mobility",
"FlagsText":"",
"Note":"Help with walking, used as four wheel walker, was content.",
"AnswerType":1,
"Fragment":"Help with walking",
"RemedialText":null,
"Details":null,
"ServiceUserID":"bc300962-3653-491a-9ba9-afab10964af4",
"ServiceUser":"Betty Test",
"ServiceUserLastName":"Test",
"ServiceUserForeNames":"Betty",
"ServiceUserDateofBirth":"19/03/1901",
"ServiceUserLocation":15,
"WorkerID":"53e6c7b9-2c80-451e-ba8c-abfb309380ac",
"Worker":"Beth Beth",
"VoidedByWorker":null,
"_supersedeStackID":null,
"SupersededByWorker":null,
"WorkerLastName":"Beth",
"DisplayOnShiftHandover":0,
"WorkerInitials":"B.B.",
"SliderData":"Walk",
"SliderData2":"Not entered",
"SliderIcons":[
{
"IconID":1093,
"CareNoteText":"was content"
},
{
"IconID":1156,
"CareNoteText":"used as four wheel walker"
}
],
"DateDone":"2019-09-30T21:24:41.994+00:00",
"DateDoneSU":"2019-09-30T21:24:41.994+00:00",
"Duration":"9 minutes",
"DurationInt":9,
"ActionIconID":6001,
"mraCareOrder":5000,
"wasPlanned":false,
"qrVerified":false,
"qrData":null,
"nfcVerified":null,
"inVerified":null,
"ViaMonitor":null
}
]
}
]'
Statement:
SELECT
j1.DateDone,
j1.Note,
j2.IconID,
j2.CareNoteText
--INTO JSONCareNotes
FROM OPENJSON(#json, '$[0].CareNotes') WITH (
DateDone nvarchar(10) '$.DateDone',
Note nvarchar(255) '$.Note',
-- add additional columns definitons
SliderIcons nvarchar(max) AS JSON
) j1
CROSS APPLY OPENJSON(j1.SliderIcons) WITH (
IconID int '$.IconID',
CareNoteText nvarchar(100) '$.CareNoteText'
) j2
Result:
DateDone Note IconID CareNoteText
2019-09-30 Help with walking, used as four wheel walker, was content. 1093 was content
2019-09-30 Help with walking, used as four wheel walker, was content. 1156 used as four wheel walker
Notes (JSON basics):
When you want to parse JSON string and get results as table, use OPENJSON table-valued function, with default or explicit schema.
Function JSON_QUERY extracts an object or an array from a JSON string. If the value is not an object or an array, the result is NULL in lax mode and an error in strict mode.
Function JSON_VALUE extracts a scalar value from a JSON string. If the path points to not a scalar value, the result is NULL in lax mode and an error in strict mode
Notes (Delphi and SQL Server):
You can organize your logic as a stored procedure, that has one parameter - the JSON text. In this situation, you'll send the JSON directly to the SQL Server and using OPENROWSET() won't be needed (OPENJSON() needs additional permissions).
Executing a stored procedure with Delphi is an easy task using ADO for example.