Lateral Flatten Snowflake from a Variant table - snowflake-cloud-data-platform

I have a variant table called raw_json, that houses multiple json files, which are unique to the ID but contain similar data points within each json. They live within the jsontext column. Here is a SS for context. I am trying to flatten each row of the raw_json table into a normal table view. The two arrays indexes need to align to assign the right values.
Here are two rows from the raw_json table and how the json is structured.
{
"ID": "PO-103",
"content": {
"EEList": [
{
"EEListID": "PO-103-1",
"EEProductID": "XXX1976",
},
{
"EEListID": "PO-103-2",
"EEProductID": "XXX1977",
},
{
"EEListID": "PO-103-3",
"EEProductID": "XXX1978",
}
],
"EENotesList": [
{
"FirstName": "John",
"LastName": "Smith",
"pxObjClass": "XX-XXSales-Work-XX"
},
{
"FirstName": "Bob",
"LastName": "Joe",
"pxObjClass": "XX-XXSales-Work-XX"
},
{
"FirstName": "Mike",
"LastName": "Smith",
"pxObjClass": "XX-XXSales-Work-XX"
}
],
}
}
{
"ID": "PO-104",
"content": {
"EEList": [
{
"EEListID": "PO-104-1",
"EEProductID": "XXX1979",
},
{
"EEListID": "PO-104-2",
"EEProductID": "XXX1980",
},
{
"EEListID": "PO-104-3",
"EEProductID": "XXX1981",
}
],
"EENotesList": [
{
"FirstName": "Sarah",
"LastName": "Butler",
"pxObjClass": "XX-XXSales-Work-XX"
},
{
"FirstName": "Jessica",
"LastName": "Adams",
"pxObjClass": "XX-XXSales-Work-XX"
}
],
}
}
into a table like this (I need this)
+--------+----------+-------------+-----------+----------+-------------------+
| ID | EEListID | EEProductID | FirstName | LastName | pxObjClass |
+--------+----------+-------------+-----------+----------+-------------------+
| PO-103 | PO-103-1 | XXX1976 | John | Smith | X-XXSales-Work-XX |
| PO-103 | PO-103-2 | XXX1977 | Bob | Joe | X-XXSales-Work-XX |
| PO-103 | PO-103-3 | XXX1978 | Mike | Smith | X-XXSales-Work-XX |
| PO-104 | PO-104-1 | XXX1979 | Sarah | Butler | X-XXSales-Work-XX |
| PO-104 | PO-104-2 | XXX1980 | Jessica | Adams | X-XXSales-Work-XX |
+--------+----------+-------------+-----------+----------+-------------------+
I have been able to flatten the EENoteList array into a table and assign the right ID to that table. Here is my code so far: Adding in EEList values without fanning out the table is where I go wrong.
select
jsontext:ID::varchar as ID,
en.value:FirstName::varchar as FirstName,
en.value:LastName::varchar as LastName,
en.value:pxObjClass::varchar as pxObjClass
-- concat(ID, EEProductID, FirstName, LastName)
from raw_json,
lateral flatten (input => jsontext:content:EENotesList, outer => false) en;
which produces this table (I have this)
+--------+-----------+----------+-------------------+
| ID | FirstName | LastName | pxObjClass |
+--------+-----------+----------+-------------------+
| PO-103 | John | Smith | X-XXSales-Work-XX |
| PO-103 | Bob | Joe | X-XXSales-Work-XX |
| PO-103 | Mike | Smith | X-XXSales-Work-XX |
| PO-104 | Sarah | Butler | X-XXSales-Work-XX |
| PO-104 | Jessica | Adams | X-XXSales-Work-XX |
| PO-104 | Terrence | Williams | X-XXSales-Work-XX |
+--------+-----------+----------+-------------------+

so mostly this answer is the preamble to get the data into a CTE, but "iff" the order of the two arrays are in lockstep, you can just use the index of the flatten to access the raw array of the other type:
WITH raw_json AS (
select PARSE_json(column1) AS jsontext FROM VALUES
('{
"ID": "PO-103",
"content": {
"EEList": [
{
"EEListID": "PO-103-1",
"EEProductID": "XXX1976",
},
{
"EEListID": "PO-103-2",
"EEProductID": "XXX1977",
},
{
"EEListID": "PO-103-3",
"EEProductID": "XXX1978",
}
],
"EENotesList": [
{
"FirstName": "John",
"LastName": "Smith",
"pxObjClass": "XX-XXSales-Work-XX"
},
{
"FirstName": "Bob",
"LastName": "Joe",
"pxObjClass": "XX-XXSales-Work-XX"
},
{
"FirstName": "Mike",
"LastName": "Smith",
"pxObjClass": "XX-XXSales-Work-XX"
}
],
}
}'), ('{
"ID": "PO-104",
"content": {
"EEList": [
{
"EEListID": "PO-104-1",
"EEProductID": "XXX1979",
},
{
"EEListID": "PO-104-2",
"EEProductID": "XXX1980",
},
{
"EEListID": "PO-104-3",
"EEProductID": "XXX1981",
}
],
"EENotesList": [
{
"FirstName": "Sarah",
"LastName": "Butler",
"pxObjClass": "XX-XXSales-Work-XX"
},
{
"FirstName": "Jessica",
"LastName": "Adams",
"pxObjClass": "XX-XXSales-Work-XX"
}
],
}
}')
)
select
jsontext:ID::varchar as ID,
en.value:FirstName::varchar as FirstName,
en.value:LastName::varchar as LastName,
en.value:pxObjClass::varchar as pxObjClass,
jsontext:content.EEList[en.index].EEListID::text as EEListID,
jsontext:content.EEList[en.index].EEProductID::text as EEProductID
from raw_json,
lateral flatten (input => jsontext:content:EENotesList, outer => false) en;
this results in:
ID FIRSTNAME LASTNAME PXOBJCLASS EELISTID EEPRODUCTID
PO-103 John Smith XX-XXSales-Work-XX PO-103-1 XXX1976
PO-103 Bob Joe XX-XXSales-Work-XX PO-103-2 XXX1977
PO-103 Mike Smith XX-XXSales-Work-XX PO-103-3 XXX1978
PO-104 Sarah Butler XX-XXSales-Work-XX PO-104-1 XXX1979
PO-104 Jessica Adams XX-XXSales-Work-XX PO-104-2 XXX1980

Related

Reactive Programming with RxJS and separating data into

I am trying to work more reactively with Angular 15 and RxJS observables for a UI component. I only subscribe to the data in my component template (html). I have a service that receives data from an external system. The issue I have is the data may be received for multiple days and needs to be 'split' for the display usage.
In the display, there are individual components of data, that show the rows returned from the service call. The service makes an HTTP call to an external host.
this.Entries$ = this.Http_.get<Array<IEntry>>('http://host.com/api/entry');
This data is then an array of records with an EntryDate, and a structure of information (UserId, Description, TimeWorked, etc.). The external API sends all the records back as one flat array of data which is not guaranteed to be sorted, it comes back in a database order, which was the order records were entered. A sort might be needed for any processing, but I am not sure.
[
{ "EnterDate": 20221025, "UserId": "JohnDoe", "TimeWorked": 2.5, ... },
{ "EnterDate": 20221025, "UserId": "JohnDoe", "TimeWorked": 4.5, ... },
{ "EnterDate": 20221025, "UserId": "BSmith", "TimeWorked": 5, ... },
{ "EnterDate": 20221026, "UserId": "JohnDoe", "TimeWorked": 4, ... },
{ "EnterDate": 20221026, "UserId": "BSmith", "TimeWorked": 5, ... },
{ "EnterDate": 20221026, "UserId": "JohnDoe", "TimeWorked": 2, ... },
]
Currently, my HTML template loops through the Entries$ observable, when it was for just one day.
<ng-container *ngFor="let OneEntry of (Entries$ | async)">
<one-entry-component [data]=OneEntry />
</ng-container>
I want to be able to split my array of records into different datasets by EntryDate (and apparently user, but just EntryDate would work for now), similar to the groupBy(), but I do not know how to get to the internal record references, as it would be a map within the groupBy() I believe.
With the data split, I would then be looking to have multiple one-day-components on the page, that then have the one-entry-component within them.
|---------------------------------------------------------------|
| |
| |-One Day 1-------------###-| |-One Day 2-------------###-| |
| | | | | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | | | | |
| |---------------------------| |---------------------------| |
| |
| |-One Day 3-------------###-| |-One Day 4-------------###-| |
| | | | | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | | | | |
| |---------------------------| |---------------------------| |
| |
|---------------------------------------------------------------|
The 4 boxes would be there if there were 4 separate days in the response. If there were 2 different dates, then just show 2 dates, but this could be 5 or 6 even.
I would need an Observable that had the dates for splitting (and even users) and then be able to pass this as data to the one<one-day-component [data]=OneDateOneUser$ />. My component needs this so that I can count the time entries for the title, which I believe is a simple .pipe(map()) operation.
Within the one-day-component, I would then simply loop through the OneDateOneUser$ observable to extract individual records to send to the one-entry-component as I do currently.
I believe the RxJS groupBy is what I need. However, I am new to RxJS, and working with the inner array of data is not clear to me in the example.
If the data is individual records like the example, and not an array of data, then it does work using the example RxJS reference.
import { of, groupBy, mergeMap, reduce, map } from 'rxjs';
of(
{ id: 1, name: 'JavaScript' },
{ id: 2, name: 'Parcel' },
{ id: 2, name: 'webpack' },
{ id: 1, name: 'TypeScript' },
{ id: 3, name: 'TSLint' }
).pipe(
groupBy(p => p.id, { element: p => p.name }),
mergeMap(group$ => group$.pipe(reduce((acc, cur) => [...acc, cur], [`${ group$.key }`]))),
map(arr => ({ id: parseInt(arr[0], 10), values: arr.slice(1) }))
)
.subscribe(p => console.log(p));
// displays:
// { id: 1, values: [ 'JavaScript', 'TypeScript' ] }
// { id: 2, values: [ 'Parcel', 'webpack' ] }
// { id: 3, values: [ 'TSLint' ] }
However, simply changing the data in the of() to be an array (more like how my data comes back), breaks, and I am not sure how to fix it:
import { of, groupBy, mergeMap, reduce, map } from 'rxjs';
of(
[
{ id: 1, name: 'JavaScript' },
{ id: 2, name: 'Parcel' },
{ id: 2, name: 'webpack' },
{ id: 1, name: 'TypeScript' },
{ id: 3, name: 'TSLint' }
]
).pipe(
groupBy(p => p.id, { element: p => p.name }),
mergeMap(group$ => group$.pipe(reduce((acc, cur) => [...acc, cur], [`${ group$.key }`]))),
map(arr => ({ id: parseInt(arr[0], 10), values: arr.slice(1) }))
)
.subscribe(p => console.log(p));
What if you just turned that Array<IEntry> into a Record<number, IEntry> with something like lodash's group by and a map RxJS operator?
Then you can get the desired outcome with some flex-wrap and flex-row functionality on the template and just loop over the entries of the record:
Check this little working CodePen
import {groupBy} from 'lodash'
const fakeData = [
{ "EnterDate": 20221025, "UserId": "JohnDoe", "TimeWorked": 2.5, ... },
{ "EnterDate": 20221025, "UserId": "JohnDoe", "TimeWorked": 4.5, ... },
{ "EnterDate": 20221025, "UserId": "BSmith", "TimeWorked": 5, ... },
{ "EnterDate": 20221026, "UserId": "JohnDoe", "TimeWorked": 4, ... },
{ "EnterDate": 20221026, "UserId": "BSmith", "TimeWorked": 5, ... },
{ "EnterDate": 20221026, "UserId": "JohnDoe", "TimeWorked": 2, ... },
]
// Replace "of" with your API call
entriesByDate$: Observable<Record<number, IEntry>> = of(fakeData).pipe(
map(allEntries => groupBy(allEntries, 'EnterDate'))
)
<div *ngIf="entriesByDate$ | async as entries" class="flex flex-row flex-wrap">
<ng-container *ngFor="let [enterDate, entries] of Object.entries(entries)">
<entry-group-component [title]="enterDate" [data]="entries" />
</ng-container>
</div>
No need to import lodash if you care to write the grouping function yourself. Array#reduce should suffice:
function groupByEnterDate(entries: Array<IEntry>) {
return entries.reduce(
(acc, current) => {
const key = current.EnterDate
const groupedByKey = acc[key] ?? []
return { ...acc, [key]: [...groupedByKey, current] }
},
{}
)
}

OVER PARTITION equivalent in MongoDB

I've simplified the scenario for brevity.
The initial data:
| EngineerId | FirstName | LastName | BirthdateOn | CupsOfCoffee | HoursOfSleep |
| ---------- | --------- | -------- | ----------- | ------------ | ------------ |
| 1 | John | Doe | 1990-01-01 | 5 | 8 |
| 2 | James | Bond | 1990-01-01 | 1 | 6 |
| 3 | Leeroy | Jenkins | 2000-06-20 | 16 | 10 |
| 4 | Jane | Doe | 2000-06-20 | 8 | 2 |
| 5 | Lorem | Ipsum | 2010-12-25 | 4 | 5 |
db.engineers.insertMany([
{ FirstName: 'John', LastName: 'Doe', BirthdateOn: ISODate('1990-01-01'), CupsOfCoffee: 5, HoursOfSleep: 8 },
{ FirstName: 'James', LastName: 'Bond', BirthdateOn: ISODate('1990-01-01'), CupsOfCoffee: 1, HoursOfSleep: 6 },
{ FirstName: 'Leeroy', LastName: 'Jenkins', BirthdateOn: ISODate('2000-06-20'), CupsOfCoffee: 16, HoursOfSleep: 10 },
{ FirstName: 'Jane', LastName: 'Doe', BirthdateOn: ISODate('2000-06-20'), CupsOfCoffee: 8, HoursOfSleep: 2 },
{ FirstName: 'Lorem', LastName: 'Ipsum', BirthdateOn: ISODate('2010-12-25'), CupsOfCoffee: 4, HoursOfSleep: 5 }
])
We want to see:
the cups of coffee consumed by the engineer
the row number sorted descending by cups of coffee
the count of engineers with the same birthdate
the sum of coffees consumed by engineers with a common birthdate
the average hours of sleep for engineers with a common birthdate
The SQL query is:
SELECT
FirstName,
LastName,
BirthdateOn,
CupsOfCoffee,
ROW_NUMBER() OVER (PARTITION BY BirthdateOn ORDER BY CupsOfCoffee DESC) AS 'Row Number',
COUNT(EngineerId) OVER (PARTITION BY BirthdateOn) AS TotalEngineers,
SUM(CupsOfCoffee) OVER (PARTITION BY BirthdateOn) AS TotalCupsOfCoffee,
AVG(HoursOfSleep) OVER (PARTITION BY BirthdateOn) AS AvgHoursOfSleep
FROM Engineers
Resulting in the following:
| FirstName | LastName | BirthdateOn | Row Number | CupsOfCoffee | TotalEngineers | TotalCupsOfCoffee | AvgHoursOfSleep |
| --------- | -------- | ----------- | ---------- | ------------ | -------------- | ----------------- | --------------- |
| John | Doe | 1990-01-01 | 1 | 5 | 2 | 6 | 7 |
| James | Bond | 1990-01-01 | 2 | 1 | 2 | 6 | 7 |
| Leeroy | Jenkins | 2000-06-20 | 1 | 16 | 2 | 24 | 6 |
| Jane | Doe | 2000-06-20 | 2 | 8 | 2 | 24 | 6 |
| Lorem | Ipsum | 2010-12-25 | 1 | 4 | 1 | 4 | 5 |
I've done quite a bit of reading on the MongoDB Aggregate Pipeline, but haven't been able to find a good solution yet. I understand that this is not SQL and the solution might not yield results in this exact format (although that would be amazing). One thing I've considered is combining the results of an aggregate and the collection, but that's either not possible or I've been searching with the wrong terms. $merge looked promising, but AFAIU it would modify the original collection and that's no good.
I've gotten as far as the following, but the results do not include the "row number", cups consumed by specific engineers, or IDs and names of the engineers.
db.engineers.aggregate([
{
$group: {
_id: '$BirthdateOn',
TotalEngineers: {
$count: { }
},
TotalCupsOfCoffee: {
$sum: '$CupsOfCoffee'
},
AvgHoursOfSleep: {
$avg: '$HoursOfSleep'
}
}
}
])
My thought with combining would be to find all of the engineers and then run the aggregate and "join" it to the engineers by BirthdateOn.
Thank you for any help! It's much appreciated.
You did a good start. To get the input data you can use with the $push operator.
Would be this:
db.engineers.aggregate([
{
$group: {
_id: "$BirthdateOn",
TotalEngineers: { $count: {} },
TotalCupsOfCoffee: { $sum: "$CupsOfCoffee" },
AvgHoursOfSleep: { $avg: "$HoursOfSleep" },
data: { $push: "$$ROOT" }
}
}
])
Regarding proper output try:
db.engineers.aggregate([
{
$group: {
_id: "$BirthdateOn",
TotalEngineers: { $count: {} },
TotalCupsOfCoffee: { $sum: "$CupsOfCoffee" },
AvgHoursOfSleep: { $avg: "$HoursOfSleep" },
data: { $push: "$$ROOT" }
}
},
{ $unwind: "$data" },
{ $replaceWith: { $mergeObjects: ["$$ROOT", "$data"] } }
])
Often it is pointless to run $group and afterwards $unwind which basically revert the former operation.
MongoDB version 5.0 introduced the $setWindowFields stage, which is quite similar to the SQL Windowing function:
I think it would be this one:
db.engineers.aggregate([
{
$setWindowFields: {
partitionBy: "$BirthdateOn",
sortBy: { CupsOfCoffee: 1 },
output: {
TotalEngineers: { $count: {} },
TotalCupsOfCoffee: { $sum: "$CupsOfCoffee" },
AvgHoursOfSleep: { $avg: "$HoursOfSleep" },
"Row Number": { $documentNumber: {} }
}
}
}
])

Laravel 8 how to join many to many relationship tables and create a query using DB

I am using Laravel 8 and I want to display list of event by joining tables that have many to many relationship.
Here is how my tables look:
Users Table
| id | firstname | status |
|----|------------|--------|
| 1 | Amy | 0 |
| 2 | 2 amy | 0 |
| 3 | 3 amy | 1 |
| 4 | 4 amy | 0 |
| 5 | 5 amy | 1 |
| 6 | 6 amy | 1 |
Here is my pivot table
events_users Table
| id | event_id | user_id |
|----|------------|---------|
| 1 | 123 | 1 |
| 1 | 123 | 2 |
| 1 | 123 | 3 |
| 1 | 123 | 4 |
Here is my events table
events Table
| id | eventid | title |
|----|------------|---------|
| 1 | 123 | title |
| 1 | 124 | title 1 |
| 1 | 125 | title 2 |
| 1 | 126 | title 3 |
Here is my model fetching the results:
$events = DB::table('events')
->join('events_users', 'events.eventid', '=', 'events_users.event_id')
->join('users', 'users.id', '=', 'events_users.user_id')
->when($sortBy, function ($query, $sortBy) {
return $query->orderBy($sortBy);
}, function ($query) {
return $query->orderBy('events.created_at', 'desc');
})
->when($search_query, function ($query, $search_query) {
return $query->where('title', 'like', '%'. $search_query . '%');
})
->select(
'title', 'eventuid', 'description', 'start_date',
'end_date', 'start_time', 'end_time', 'status',
'venue', 'address_line_1', 'address_line_2', 'address_line_3',
'postcode', 'city', 'city_id', 'country', 'image',
'users.firstname', 'users.lastname', 'users.avatar'
)
->simplePaginate(15);
This results in duplicate entries:
Current Result:
{
"current_page": 1,
"data": [
{
"title": "Who in the newspapers, at the mushroom (she had.",
"eventuid": "be785bac-70d5-379f-a6f8-b35e66c8e494",
"description": "I'd been the whiting,' said Alice, 'and why it is I hate cats and dogs.' It was opened by another footman in livery came running out of sight before the trial's over!' thought Alice. 'I'm glad they.",
"start_date": "2000-11-17",
"end_date": "1988-02-24",
"start_time": "1972",
"end_time": "2062",
"status": 1,
"venue": "4379",
"address_line_1": "Kuhn Expressway",
"address_line_2": "2295 Kerluke Drive Suite 335",
"address_line_3": "Fredtown",
"postcode": "57094",
"city": "New Cassidyburgh",
"city_id": 530,
"country": "Cocos (Keeling) Islands",
"image": "https://via.placeholder.com/1280x720.png/00dd99?text=repellat",
"firstname": "Marielle",
"lastname": "Tremblay",
"avatar": "https://via.placeholder.com/640x480.png/002277?text=eum"
},
{
"title": "Who in the newspapers, at the mushroom (she had.",
"eventuid": "be785bac-70d5-379f-a6f8-b35e66c8e494",
"description": "I'd been the whiting,' said Alice, 'and why it is I hate cats and dogs.' It was opened by another footman in livery came running out of sight before the trial's over!' thought Alice. 'I'm glad they.",
"start_date": "2000-11-17",
"end_date": "1988-02-24",
"start_time": "1972",
"end_time": "2062",
"status": 1,
"venue": "4379",
"address_line_1": "Kuhn Expressway",
"address_line_2": "2295 Kerluke Drive Suite 335",
"address_line_3": "Fredtown",
"postcode": "57094",
"city": "New Cassidyburgh",
"city_id": 530,
"country": "Cocos (Keeling) Islands",
"image": "https://via.placeholder.com/1280x720.png/00dd99?text=repellat",
"firstname": "Floyd",
"lastname": "Waelchi",
"avatar": "https://via.placeholder.com/640x480.png/0033cc?text=inventore"
},
...
]
}
What I want to retrieve is something like this:
Expecting:
{
"current_page": 1,
"data": [
{
"title": "Who in the newspapers, at the mushroom (she had.",
"eventuid": "be785bac-70d5-379f-a6f8-b35e66c8e494",
"description": "I'd been the whiting,' said Alice, 'and why it is I hate cats and dogs.' It was opened by another footman in livery came running out of sight before the trial's over!' thought Alice. 'I'm glad they.",
"start_date": "2000-11-17",
"end_date": "1988-02-24",
"start_time": "1972",
"end_time": "2062",
"status": 1,
"venue": "4379",
"address_line_1": "Kuhn Expressway",
"address_line_2": "2295 Kerluke Drive Suite 335",
"address_line_3": "Fredtown",
"postcode": "57094",
"city": "New Cassidyburgh",
"city_id": 530,
"country": "Cocos (Keeling) Islands",
"image": "https://via.placeholder.com/1280x720.png/00dd99?text=repellat",
"users" : {[
{
"firstname": "Marielle",
"lastname": "Tremblay",
"avatar": "https://via.placeholder.com/640x480.png/002277?text=eum"
},
{
"firstname": "Amy",
"lastname": "Bond",
"avatar": "https://via.placeholder.com/640x480.png/005277?text=eum"
}
]}
},
...
]
}

I need to flatten JSON data in Snowflake Table1 table, in one JSON_DATA column that has an array

Could somebody help me to create an SQL statement to flatten JSON data in Snowflake Table1 table, in one JSON_DATA column that has an array?
JSON_DATA:
{
"scopes": [
{
"scope_name": "IN SCOPE",
"company_code": "01",
"lob_codes": ["01","07","09"]
},
{
"scope_name": "IN SCOPE",
"company_code": "02",
"lob_codes": ["07","13","20"]
},
{
"scope_name": "OUT OF SCOPE",
"company_code": "01",
"lob_codes": ["30","35","40"]
},
{
"scope_name": "OUT OF SCOPE",
"company_code": "02",
"lob_codes": ["02","03","05"]
}
]
}
I need to flatten it to:
|scope_name | company_code| lob_codes|
|--------------|----------------|------------|
|IN SCOPE | 1 | 01 |
|IN SCOPE | 1 | 07 |
|IN SCOPE | 1 | 09 |
|IN SCOPE | 2 | 07 |
|IN SCOPE | 2 | 13 |
|IN SCOPE | 2 | 20 |
|OUT OF SCOPE | 1 | 30 |
|OUT OF SCOPE | 1 | 35 |
|OUT OF SCOPE | 1 | 40 |
|OUT OF SCOPE | 2 | 02 |
|OUT OF SCOPE | 2 | 03 |
|OUT OF SCOPE | 2 | 05 |
I believe you are looking for something along these lines. Focus on the SELECT statement at the end. The top piece is just emulating the data you provided.
WITH x AS (
SELECT parse_json('{
"scopes": [
{
"scope_name": "IN SCOPE",
"company_code": "01",
"lob_codes": ["01","07","09"]
},
{
"scope_name": "IN SCOPE",
"company_code": "02",
"lob_codes": ["07","13","20"]
},
{
"scope_name": "OUT OF SCOPE",
"company_code": "01",
"lob_codes": ["30","35","40"]
},
{
"scope_name": "OUT OF SCOPE",
"company_code": "02",
"lob_codes": ["02","03","05"]
}
]
}') as json_data
)
SELECT
y.value:company_code::varchar as company_code,
y.value:scope_name::varchar as scope_name,
z.value::varchar as lob_codes
FROM x,
LATERAL FLATTEN (input=>x.json_data:scopes) y,
LATERAL FLATTEN (input=>y.value:lob_codes) z;

How can i access the elements from multi dimensional json array (in SQL Server)

I have a multidimensional JSON array, I am accessing the JSON array in SQL Server and using 'OPENJSON' to convert JSON data to SQL. I am currently facing problem in fetching the data from multidimensional array
Declare #Json nvarchar(max)
Set #Json= '[{
"id": 0,
"healthandSafety": "true",
"estimationCost": "7878",
"comments": "\"Comments\"",
"image": [{
"imageData": "1"
}, {
"imageData": "2"
}, {
"imageData": "3"
}, {
"imageData": "4"
}, {
"imageData": "5"
}]
}, {
"id": 1,
"healthandSafety": "false",
"estimationCost": "90",
"comments": "\"89089\"",
"image": [{
"imageData": "6"
}, {
"imageData": "7"
}, {
"imageData": "8"
}, {
"imageData": "9"
}, {
"imageData": "10"
}, {
"imageData": "11"
}]
}]'
Select ImageJsonFile from OPENJSON (#Json) with (ImageJsonFile nvarchar(max) '$.image[0].imageData')
When I tried the above code I obtained the following output:
ImageJsonFile
1
6
The output what I am expecting :
ImageJsonFile
1
2
3
4
5
You need to define query path:
Select * from OPENJSON (#Json,'$[0].image') with (ImageJsonFile nvarchar(max) '$.imageData')
You've got an answer already, so this is just to add some more details:
The following will bring back all data from your multi dimensional array, not just one array index you'd have to specify explictly.
DECLARE #Json NVARCHAR(MAX)=
N'[{
"id": 0,
"healthandSafety": "true",
"estimationCost": "7878",
"comments": "\"Comments\"",
"image": [{
"imageData": "1"
}, {
"imageData": "2"
}, {
"imageData": "3"
}, {
"imageData": "4"
}, {
"imageData": "5"
}]
}, {
"id": 1,
"healthandSafety": "false",
"estimationCost": "90",
"comments": "\"89089\"",
"image": [{
"imageData": "6"
}, {
"imageData": "7"
}, {
"imageData": "8"
}, {
"imageData": "9"
}, {
"imageData": "10"
}, {
"imageData": "11"
}]
}]';
--The query
SELECT A.id
,A.healthandSafety
,A.estimationCost
,A.comments
,B.imageData
FROM OPENJSON(#Json)
WITH(id INT
,healthandSafety BIT
,estimationCost INT
,comments NVARCHAR(1000)
,[image] NVARCHAR(MAX) AS JSON ) A
CROSS APPLY OPENJSON(A.[image])
WITH(imageData INT) B;
The result
+----+-----------------+----------------+----------+-----------+
| id | healthandSafety | estimationCost | comments | imageData |
+----+-----------------+----------------+----------+-----------+
| 0 | 1 | 7878 | Comments | 1 |
+----+-----------------+----------------+----------+-----------+
| 0 | 1 | 7878 | Comments | 2 |
+----+-----------------+----------------+----------+-----------+
| 0 | 1 | 7878 | Comments | 3 |
+----+-----------------+----------------+----------+-----------+
| 0 | 1 | 7878 | Comments | 4 |
+----+-----------------+----------------+----------+-----------+
| 0 | 1 | 7878 | Comments | 5 |
+----+-----------------+----------------+----------+-----------+
| 1 | 0 | 90 | 89089 | 6 |
+----+-----------------+----------------+----------+-----------+
| 1 | 0 | 90 | 89089 | 7 |
+----+-----------------+----------------+----------+-----------+
| 1 | 0 | 90 | 89089 | 8 |
+----+-----------------+----------------+----------+-----------+
| 1 | 0 | 90 | 89089 | 9 |
+----+-----------------+----------------+----------+-----------+
| 1 | 0 | 90 | 89089 | 10 |
+----+-----------------+----------------+----------+-----------+
| 1 | 0 | 90 | 89089 | 11 |
+----+-----------------+----------------+----------+-----------+
The idea in short:
We use the first OPENJSON to get the elements of the first level. The WITH clause will name all elements and return the [image] with NVARCHAR(MAX) AS JSON. This allows to use another OPENJSON to read the numbers from imageData, your nested dimension, while the id-column is the grouping key.

Resources