Extract a JSON array items to a single column separated with comma - sql

Struggling to find an answer to the below JSON problem. I would like to display the entire SKU/Quantity list in the "shipmentItems" array to their respective column with a comma-separated value. My example below only allows me to display the first SKU/quantity from the array but, my goal is to get all listed in the columns with comma-separated.
JSON example:
{"shipments": [
{
"shipmentId": 100003768,
"orderNumber": "9648219086",
"shipDate": "2021-10-28",
"serviceCode": "ups_ground",
"shipmentItems": [
{
"orderItemId": 1464643208,
"lineItemKey": "10322938560608",
"sku": "SCPXTSS-BAG-06",
"name": "SCOOP-PLATE-06 (1000ml)",
"weight": {
"value": 0,
"units": "ounces",
"WeightUnits": 1
},
"quantity": 1,
"unitPrice": 0,
"taxAmount": null
},
{
"orderItemId": 1464643207,
"lineItemKey": "10322938527840",
"sku": "SCPZRTS-TRAY-01",
"name": "Beef: Tray 3 (Fill 004)<br>",
"weight": {
"value": 60,
"units": "ounces",
"WeightUnits": 1
},
"quantity": 1,
"unitPrice": 102.72,
"taxAmount": null
}
],
"labelData": null,
"formData": null
}
]
}
SQL query I'm using:
DECLARE #JSON varchar(max)
SELECT #JSON = BulkColumn
FROM OPENROWSET (BULK 'C:\Users\XPS-LT\json\today\shipments_20211031.json', SINGLE_CLOB)
IMPORT
SELECT *
FROM OPENJSON (#JSON, '$.shipments')
WITH
(
[shipmentId] bigint,
[orderNumber] nvarchar(60),
[shipDate] date,
[serviceCode] nvarchar(30),
[sku] nvarchar(MAX) N'$.shipmentItems[0].sku',
[quantity] int N'$.shipmentItems[0].quantity'
)
;

The "shipmentItems" part of the input JSON is an array, so you need an AS JSON clause in the first explicit schema and an additional OPENJSON() call:
DECLARE #json nvarchar(max)
...
SELECT
j.[shipmentId], j.[orderNumber], j.[shipDate], j.[serviceCode],
a.[sku], a.[quantity]
FROM OPENJSON (#json, '$.shipments') WITH (
[shipmentId] bigint,
[orderNumber] nvarchar(60),
[shipDate] date,
[serviceCode] nvarchar(30),
[shipmentItems] nvarchar(max) AS JSON
) j
OUTER APPLY (
SELECT
STRING_AGG([sku], ',') WITHIN GROUP (ORDER BY [orderItemId]),
STRING_AGG([quantity], ',') WITHIN GROUP (ORDER BY [orderItemId])
FROM OPENJSON (j.shipmentItems) WITH (
[orderItemId] int '$.orderItemId',
[sku] nvarchar(max) '$.sku',
[quantity] int N'$.quantity'
)
) a ([sku], [quantity])
Result:
shipmentId orderNumber shipDate serviceCode sku quantity
100003768 9648219086 2021-10-28 ups_ground SCPZRTS-TRAY-01,SCPXTSS-BAG-06 1,1

Related

SQL JSON query to extract an object

I think the problem is because I have hard brackets [] in the 'groups' object.
JSON:
declare #json nvarchar(max) = N'{ "agents": [ {
"id": 9544321,
"uuid":"xxxx-xxx-xxxx",
"groups": [
{
"name": "GROUP NAME HERE",
"id": 123456
}
],
"support": true
}]}'
What I'm trying to get to is:
id
uuid
group name
group id
9544321
xxxx-xxx-xxxx
GROUP NAME HERE
123456
And then if there is a second group specified in the 'groups' object that'd be two rows. etc
This is my code so far, but it just leaves the [group name] and [group id] columns blank. I've also tried a cross apply and that didn't work (same result).
select *
from openjson(#json, '$.agents')
with (
id int '$.id',
uuid varchar(60) '$.uuid',
groups nvarchar(max) '$.groups' as json
)
outer apply openjson (groups)
with ([group name] nvarchar(max) '$',
[group id] int '$'
)
Try this:
select *
from openjson(#json, '$.agents')
with (
id int '$.id',
uuid varchar(60) '$.uuid',
groups nvarchar(max) '$.groups' as json
)
outer apply openjson (groups, '$')
with ([name] nvarchar(max),
[id] int
)
Did you find the difference? You have placed the value 'Group Name' which is the value, instead of keys in json.
#BeckyG, you're almost there. Please, pay attention to lines 29 and 30:
DECLARE #json NVARCHAR(MAX) = N'{ "agents": [ {
"id": 9544321,
"uuid":"xxxx-xxx-xxxx",
"groups": [
{
"name": "GROUP NAME HERE",
"id": 123456
},
{
"name": "GROUP NAME HERE 2",
"id": 1234567
}
],
"support": true
}]}';
SELECT agents.id,
agents.uuid,
groups.name [group name],
groups.id [group id]
FROM OPENJSON(#json, '$.agents')
WITH (
id INT '$.id',
uuid VARCHAR(60) '$.uuid',
groups NVARCHAR(MAX) '$.groups' AS JSON
) AS agents
OUTER APPLY OPENJSON (agents.groups)
WITH (
[name] NVARCHAR(MAX) '$.name',
[id] INT '$.id'
) AS groups;
Happy coding!

Using multiple Cross Joins with JSON seems to slow my query right down and never completes

I have the following JSON:
[
{
"UserDetails": [
{
"UserName": "User1",
"UserDateOfBirth": "06/11/89",
"UserID": "12345",
"NotesDay1": [
{
"NoteID": "a287fcc4",
"AnswerType": 1,
"RemedialText": null,
"Details": null,
"UserLocation": 0,
"DateDone": "2021-08-06T00:19:14"
},
{
"NoteID": "4a48385a",
"AnswerType": 1,
"RemedialText": null,
"Details": null,
"UserLocation": 0,
"DateDone": "2021-08-06T02:19:59"
},
{
"NoteID": "ddb6bc52",
"AnswerType": 1,
"RemedialText": null,
"Details": null,
"UserLocation": 0,
"DateDone": "2021-08-06T04:14:50"
}
]
},
{
"UserName": "User2",
"UserDateOfBirth": "05/08/99",
"UserID": "23456",
"NotesDay1": [],
"NotesDay2": [
{
"NoteID": "62cf5478",
"AnswerType": 1,
"RemedialText": null,
"Details": null,
"UserLocation": 0,
"DateDone": "2021-08-07T01:00:48"
},
{
"NoteID": "7f864ef4",
"AnswerType": 1,
"RemedialText": null,
"Details": null,
"UserLocation": 0,
"DateDone": "2021-08-07T01:00:48"
},
{
"NoteID": "db1a0af0",
"AnswerType": 1,
"RemedialText": null,
"Details": null,
"UserLocation": 0,
"DateDone": "2021-08-07T06:28:02"
}
],
"NotesDay3": [
{
"NoteID": "2ae6b923",
"AnswerType": 1,
"RemedialText": null,
"Details": null,
"UserLocation": 0,
"DateDone": "2021-08-07T06:28:02"
}
],
"NotesDay4": [],
"NotesDay5": [],
"NotesDay6": [],
"NotesDay7": []
}
]
}
]
My current SQL query to import this is set up as below:
SELECT j2.UserID, j3.NoteID1, j4.NoteID2, j5.NoteID3, j6.NoteID4, j7.NoteID5, j8.NoteID6, j9.NoteID7
INTO [UserDayNotes]
FROM OPENJSON(#JSON)
WITH
(
UserDetails nvarchar(max) '$.UserDetails' as JSON
) j1
CROSS APPLY OPENJSON(j1.UserDetails) WITH
(
UserID nvarchar(100) '$.UserID',
NotesDay1 nvarchar(max) '$.NotesDay1' as JSON,
NotesDay2 nvarchar(max) '$.NotesDay2' as JSON,
NotesDay3 nvarchar(max) '$.NotesDay3' as JSON,
NotesDay4 nvarchar(max) '$.NotesDay4' as JSON,
NotesDay5 nvarchar(max) '$.NotesDay5' as JSON,
NotesDay6 nvarchar(max) '$.NotesDay6' as JSON,
NotesDay7 nvarchar(max) '$.NotesDay7' as JSON
) j2
CROSS APPLY OPENJSON(j2.NotesDay1) WITH
(
NoteID1 nvarchar(100) '$.NoteID'
) j3
CROSS APPLY OPENJSON(j2.CareNotesDay2) WITH
(
NoteID2 nvarchar(100) '$.NoteID'
) j4
CROSS APPLY OPENJSON(j2.CareNotesDay3) WITH
(
NoteID3 nvarchar(100) '$.NoteID'
) j5
CROSS APPLY OPENJSON(j2.CareNotesDay4) WITH
(
NoteID4 nvarchar(100) '$.NoteID'
) j6
CROSS APPLY OPENJSON(j2.CareNotesDay5) WITH
(
NoteID5 nvarchar(100) '$.NoteID'
) j7
CROSS APPLY OPENJSON(j2.CareNotesDay6) WITH
(
NoteID6 nvarchar(100) '$.NoteID'
) j8
CROSS APPLY OPENJSON(j2.CareNotesDay7) WITH
(
NoteID3 nvarchar(100) '$.NoteID'
) j9
If I run the query as it is, then it runs forever and ever and never completes. If I comment out all but j2.UserID, j3.NoteID1 and j4.NoteID2, then the query runs more or less instantly. I'm assuming this is something to do with the number of cross apply items I have, but my experience and knowledge of this side of SQL is not great, and I could really use some help.
After discussions below, I've realised I could have multiple instances of Day1, Day 2 etc and so a more realistic desired output would be below:
UserID
DayNumber
NoteID
User1
1
a287fcc4
User1
1
4a48385a
User1
1
ddb6bc52
User2
2
62cf5478
User2
2
7f864ef4
User2
2
db1a0af0
User2
3
2ae6b923
(Good lord, that table took some doing!)
The data inside each NotesDayX object is a repeat of information held elsewhere, so as long as I have the NoteID then I can dispense with the other info contained within the JSON.
Thanks in advance.
You could union all the Notes arrays together.
Furthermore, it's unclear if the outer array always has only a single inner object, if so then we can use $[0] and skip the first OPENJSON
SELECT
j1.UserID,
j2.DayNumber,
j2.NoteID
INTO [UserDayNotes]
FROM OPENJSON(#JSON, '$[0].UserDetails')
WITH
(
UserID nvarchar(100),
NotesDay1 nvarchar(max) as JSON,
NotesDay2 nvarchar(max) as JSON,
NotesDay3 nvarchar(max) as JSON,
NotesDay4 nvarchar(max) as JSON,
NotesDay5 nvarchar(max) as JSON,
NotesDay6 nvarchar(max) as JSON,
NotesDay7 nvarchar(max) as JSON
) j1
CROSS APPLY (
SELECT 1 AS DayNumber, NoteID
FROM OPENJSON(j1.NotesDay1) WITH
(
NoteID nvarchar(100)
) j2
UNION ALL
SELECT 2 AS DayNumber, NoteID
FROM OPENJSON(j1.NotesDay2) WITH
(
NoteID nvarchar(100)
) j2
UNION ALL
SELECT 3 AS DayNumber, NoteID
FROM OPENJSON(j1.NotesDay3) WITH
(
NoteID nvarchar(100)
) j2
UNION ALL
SELECT 4 AS DayNumber, NoteID
FROM OPENJSON(j1.NotesDay4) WITH
(
NoteID nvarchar(100)
) j2
UNION ALL
SELECT 5 AS DayNumber, NoteID
FROM OPENJSON(j1.NotesDay5) WITH
(
NoteID nvarchar(100)
) j2
UNION ALL
SELECT 6 AS DayNumber, NoteID
FROM OPENJSON(j1.NotesDay6) WITH
(
NoteID nvarchar(100)
) j2
UNION ALL
SELECT 7 AS DayNumber, NoteID
FROM OPENJSON(j1.NotesDay7) WITH
(
NoteID nvarchar(100)
) j2
) j2;
..if the json structure is slightly different and if there can be only one object in the userdetails and notesdayX arrays …fiddle:
declare #j nvarchar(max) =N'
[
{"UserDetails":[
{"UserID":"User1",
"NotesDay1":[{"NoteID":"12345"}],
"NotesDay2":[],
"NotesDay3":[],
"NotesDay4":[{"NoteID":"23456"}],
"NotesDay5":[],
"NotesDay6":[{"NoteID":"34567"}],
"NotesDay7":[{"NoteID":"45678"}]
}
]},
{"UserDetails":[
{"UserID":"User2",
"NotesDay1":[{"NoteID":"54321"}],
"NotesDay2":[{"NoteID":"65432"}],
"NotesDay3":[],
"NotesDay4":[{"NoteID":"76543"}],
"NotesDay5":[],
"NotesDay6":[],
"NotesDay7":[{"NoteID":"87654"}]
}
]}
]';
select *
from openjson(#j)
with
(
userid nvarchar(100) '$.UserDetails[0].UserID',
notesday1 nvarchar(10) '$.UserDetails[0].NotesDay1[0].NoteID',
notesday4 nvarchar(10) '$.UserDetails[0].NotesDay4[0].NoteID',
notesday7 nvarchar(10) '$.UserDetails[0].NotesDay7[0].NoteID'
);

Parse JSON structure in SQL

I have written a code that reads information from JSON lines to table in SQL:
declare #pJSON varchar(max) = '
{
"School": "MiddleSchool",
"Password": "SchoolPassword",
"Attributes": [
{
"Type": "Exam",
"Value": "1"
},
{
"Type": "Class",
"Value": "11b"
},
{
"Type": "Math",
"Value": [
{
"ExamDate": "2019-01-01",
"Points": 100,
"Grade": 10,
"Notes": "Good"
}
]
}
]
} '
select ExamDate, Points, Grade, Notes
from OPENJSON(#pJSON, N'$.Attributes[2].Value')
cross apply openjson ([Value])
with
(
ExamDate date,
Points int,
Grade int,
Notes varchar(max)
) as [value]
Code works fine, but I really hate N'$.Attributes[2].Value' part. Exam information can be in the first, second, third place, so [2] doesn't really work for me. Do you have any suggestions for me, how I can improve this code? Thank you!
You could use JSON_QUERY:
select ExamDate, Points, Grade, Notes
from OPENJSON(JSON_QUERY(#pJSON, N'$.Attributes'))
with
(
ExamDate date N'$.Value[0].ExamDate', -- here 0 because Value is array too
Points int N'$.Value[0].Points',
Grade int N'$.Value[0].Grade',
Notes varchar(max) N'$.Value[0].Notes'
) as [value]
WHERE ExamDate IS NOT NULL;
db<>fiddle demo
EDIT:
In original question there was only one exam in array. If array could contain more than code should be adjusted:
SELECT s2.[key]
,ExamValue = JSON_VALUE(s2.value, '$.ExamDate')
,Points = JSON_VALUE(s2.value, '$.Points')
,Grade = JSON_VALUE(s2.value, '$.Grade')
,Notes = JSON_VALUE(s2.value, '$.Notes')
FROM OPENJSON(JSON_QUERY(#pJSON, N'$.Attributes')) s
CROSS APPLY OPENJSON(JSON_QUERY(s.value, N'$.Value')) s2;
-- or
SELECT [value].*
FROM OPENJSON(JSON_QUERY(#pJSON, N'$.Attributes'))
CROSS APPLY OPENJSON(JSON_QUERY(value, N'$.Value'))
with
(
ExamDate date N'$.ExamDate',
Points int N'$.Points',
Grade int N'$.Grade',
Notes varchar(max) N'$.Notes'
) as [value];
db<>fiddle demo

Parsing JSON data to find one element

I'm sifting through a JSON movie database and can get most of the data without a problem. For each film, I've put the raw JSON in an nvarchar(max) column called jsondata, then added other columns to populate for indexing and quick retrieval purposes.
With my UPDATE statement, I'm able to populate all columns except for one, in which I need to parse through and find an element.
Here's an example of the JSON:
{
"title": "Amnesia Love",
"genres": [{
"id": 35,
"name": "Comedy"
}],
"id": 508989,
"original_language": "tl",
"overview": "A guy is trying to discover his true identity after being found unconscious in a remote island.",
"popularity": 2.583,
"release_date": "2018-02-28",
"credits": {
"cast": [{
"cast_id": 2,
"character": "",
"credit_id": "5a9af6ecc3a3680b7d024627",
"gender": 0,
"id": 1230955,
"name": "Paolo Ballesteros",
"order": 1,
"profile_path": "/8Cey11JPMWBCGuIcKBXvb6OQ7Je.jpg"
},
{
"cast_id": 3,
"character": "",
"credit_id": "5a9af6f3c3a3680b57024465",
"gender": 0,
"id": 1166094,
"name": "Yam Concepcion",
"order": 2,
"profile_path": "/fiPaXTkq440VHXDqlMRHtOpoLWT.jpg"
}],
"crew": [{
"credit_id": "5a9af6cc0e0a260649024c6a",
"department": "Directing",
"gender": 0,
"id": 1989658,
"job": "Director",
"name": "Albert Langitan",
"profile_path": null
},
{
"credit_id": "5a9af6dec3a3680b2d01f152",
"department": "Writing",
"gender": 0,
"id": 1989658,
"job": "Screenplay",
"name": "Albert Langitan",
"profile_path": null
}]
},
}
Here's the table schema:
CREATE TABLE dbo.moviedb(
id int IDENTITY(1,1) NOT NULL,
jsondata nvarchar(max) NULL,
title nvarchar(200) NULL,
movie_id varchar(255) NULL,
original_language char(2) NULL,
overview nvarchar(1000) NULL,
popularity float NULL,
release_date datetime NULL,
genre nvarchar(100) NULL,
director nvarchar(100)
)
Here is an update statement to populate the columns:
UPDATE t
SET t.title = j.title, t.movie_id = j.id, t.original_language = j.original_language,
t.overview = j.overview, t.popularity = j.popularity, t.release_date = j.release_date,
t.genre = ISNULL(JSON_VALUE(t.jsondata,'$.genres[0].name'),''),
t.director = JSON_VALUE(t.jsondata,'$.credits.crew[0].name')
FROM tmdb t
CROSS APPLY OPENJSON(jsondata)
WITH(title nvarchar(200), id int, original_language char(2), overview nvarchar(max),
popularity float, release_date datetime,
genres nvarchar(max) as JSON,
credits nvarchar(max) as JSON
) AS j
My problem is getting the director name. Since within $.credits, there are cast and crew elements, and within those, potentially many entries each - I'm not clear how to parse through via JSON_QUERY or using CROSS APPLY to basically say, 'find the $.credits.crew.job = "Director", then give me the $.credits.crew.name'.
Ok, after the comments, I think I've got it now. I use OUTER APPLY for the Director, since I still want to get the movie info even if no Director is specified. This lead me to one other issue - when there was more than 1 Director on the movie. But I think I can deal with that.
UPDATE t
SET t.title = j.title, t.movie_id = j.id, t.original_language = j.original_language,
t.overview = j.overview, t.popularity = j.popularity, t.release_date = j.release_date,
t.genre = ISNULL(JSON_VALUE(t.jsondata,'$.genres[0].name'),''),
t.director = k.name
FROM tmdb t
CROSS APPLY OPENJSON(jsondata)
WITH (title nvarchar(200), id int, original_language char(2),
overview nvarchar(max), popularity float, release_date datetime,
credits nvarchar(max) as JSON
) AS j
OUTER APPLY OPENJSON(j.credits,'$.crew')
WITH (job nvarchar(50), name nvarchar(100)) AS k
WHERE k.job = 'Director' OR k.job IS NULL

Json functions in SQL Server 2016

Here is my JSON
[{
"type": "FormBlock",
"id": "07bac163-1765-1fee-dba7-6668f8d8507f",
"x": 50,
"y": 57,
"width": 120,
"height": 50,
"alpha": 1,
"angle": 0,
"userData": {
"schema":"{"form":[{"id":"1493828935122"},{"id":"1495115355556"}]
}]
My query
SELECT JSON_VALUE((SELECT JSON_QUERY([Schema].[schema],'$[0]')
FROM [dbo].[Schema] WHERE objecttype='test'),'$.userData.schema.form[0].id')
[Schema].[schema] : Table [Schema] with column [schema] (contain a json)
I can get userData.schema data, but if i want to have userData.schema.form.id it doesn't want to work. Why?
Assume you have the following document stored in SQL:
CREATE TABLE JSONTable (
ID int IDENTITY (1,1) PRIMARY KEY CLUSTERED
,JSONDocument nvarchar(max) )
INSERT INTO JSONTable
SELECT '{
"FilmDetails":{
"ProductNumber":"9912088751",
"Title":"Brave",
"Type":"Movie",
"Runtime":93,
"ReleaseYear":2012,
"Synopses":[
{
"Locale":"en",
"Text":"Princess Merida uses her bravery and archery skills to battle a curse and restore peace..."
},
{
"Locale":"de",
"Text":"Animiert"
},
{
"Locale":"fr",
"Text":"Au coeur des contrées sauvages dÉcosse, Merida, la fille du roi Fergus et de la reine Elinor..."}],
"Assets":[
{
"Name":"Stickers",
"AssetType":"Stickers",
"FileType":"PDF",
"Location":"http://media.brave.stickers.pdf",
"Locale":"en-US"
},
{
"Name":"Trailer - WMV",
"AssetType":"Trailer - WMV",
"FileType":"Video",
"Location":"http://youtu.be/Shg79Shgn",
"Locale":"en-US"
}]
}
}'
You can query in to arrays like such:
SELECT
JSON_VALUE(JSONDocument, '$.FilmDetails.ProductNumber') as ProductNumber
,JSON_VALUE(JSONDocument, '$.FilmDetails.Title') as Title
,JSON_VALUE(JSONDocument, '$.FilmDetails.Type') as ContentType
,JSON_VALUE(JSONDocument, '$.FilmDetails.Runtime') as Runtime
,JSON_VALUE(JSONDocument, '$.FilmDetails.ReleaseYear') as ReleaseYear
,Locale
,SynopsesText
,Name AS AssetName
,FileType AS AssetFileType
,[Location] AS AssetLocation
FROM JSONTable
CROSS APPLY OPENJSON(JSONDocument, '$.FilmDetails.Synopses')
WITH (
Locale varchar(3) '$.Locale'
,SynopsesText nvarchar(2000) '$.Text')
CROSS APPLY OPENJSON(JSONDocument, '$.FilmDetails.Assets')
WITH (
Name varchar(25) '$.Name'
,FileType varchar(25) '$.FileType'
,[Location] nvarchar(500) '$.Location' )
WHERE JSON_VALUE(JSONDocument, '$.FilmDetails.Title') LIKE '%Brave%'
AND Locale = 'en'
AND FileType = 'video'
This is from a blog post I made awhile back, but I think it gives you what you are looking for, querying in to arrays.