DB2 Building json using JSON_ARRAY - db2

I am using DB2LUW 11.5.
I am building a JSON under usage of the below tables and want an output like this
I tried many things but I come not up to a solution.
{
"ID": 1,
"NAME": "a",
"B_OBJECTS": [{
"ID": 1,
"SIZE": 5
}, {
"ID": 2,
"SIZE": 10
}, {
"ID": 3,
"SIZE": 15
}
],
"C_OBJECTS": [{
"ID": 1,
"SIZE": 100
}, {
"ID": 2,
"SIZE": 200
}
]
}
Table_A
ID
NAME
1
a
Table_B
ID
ID_A
SIZE
1
1
5
2
1
10
3
1
15
Table_C
ID
ID_A
SIZE
1
1
100
2
1
200
WITH
TABLE_A(ID,NAME) AS
(
VALUES (1, 'a')
)
, TABLE_B(ID, ID_A, SIZE) AS
(
VALUES (1, 1, 5), (2, 1, 10), (3, 1, 15)
), TABLE_C(ID, ID_A, SIZE) AS
(
VALUES (1, 1, 100), (2,1, 200)
)
, JSON_STEP_1 AS
(
SELECT A_ID, A_NAME, B_ID, C_ID
, JSON_OBJECT('ID' VALUE B_ID, 'SIZE' VALUE B_SIZE) B_JSON
, JSON_OBJECT('ID' VALUE C_ID, 'SIZE' VALUE C_SIZE) C_JSON
FROM
(
SELECT
A.ID AS A_ID, A.NAME AS A_NAME, B.ID AS B_ID, B.SIZE AS B_SIZE, C.ID AS C_ID, C.SIZE AS C_SIZE
FROM TABLE_A A
JOIN TABLE_B B ON B.ID_A = A.ID
JOIN TABLE_C C ON C.ID_A = A.ID
)
GROUP BY A_ID, A_NAME, B_ID, B_SIZE, B_ID, B_SIZE, C_ID, C_SIZE
)
, JSON_STEP_2 AS
(
SELECT
JSON_OBJECT
(
'ID' VALUE A_ID,
'NAME' VALUE A_NAME,
'B_OBJECTS' VALUE JSON_ARRAY (LISTAGG(B_JSON, ', ') WITHIN GROUP (ORDER BY B_ID) FORMAT JSON) FORMAT JSON,
'C_OBJECTS' VALUE JSON_ARRAY (LISTAGG(C_JSON, ', ') WITHIN GROUP (ORDER BY C_ID) FORMAT JSON) FORMAT JSON
) JSON_OBJS
FROM JSON_STEP_1
GROUP BY A_ID, A_NAME
)
SELECT * FROM JSON_STEP_2
I get a mulitplication of the results
{
"ID": 1,
"NAME": "a",
"B_OBJECTS": [{
"ID": 1,
"SIZE": 5
}, {
"ID": 1,
"SIZE": 5
}, {
"ID": 2,
"SIZE": 10
}, {
"ID": 2,
"SIZE": 10
}, {
"ID": 3,
"SIZE": 15
}, {
"ID": 3,
"SIZE": 15
}
],
"C_OBJECTS": [{
"ID": 1,
"SIZE": 100
}, {
"ID": 1,
"SIZE": 100
}, {
"ID": 1,
"SIZE": 100
}, {
"ID": 2,
"SIZE": 200
}, {
"ID": 2,
"SIZE": 200
}, {
"ID": 2,
"SIZE": 200
}
]
}

You have to build arrays from table B and C in different queries.
By the way, since your version is 11.5 you will maybe sometime have JSON_ARRAYAGG available
So you can write :
WITH
TABLE_A(ID,NAME) AS
(
VALUES (1, 'a')
)
, TABLE_B(ID, ID_A, SIZE) AS
(
VALUES (1, 1, 5), (2, 1, 10), (3, 1, 15)
), TABLE_C(ID, ID_A, SIZE) AS
(
VALUES (1, 1, 100), (2,1, 200)
)
select
JSON_OBJECT
(
'ID' VALUE A.ID,
'NAME' VALUE A.NAME,
'B_OBJECTS' VALUE (
select json_arrayagg(
JSON_OBJECT('ID' VALUE b.ID, 'SIZE' VALUE b.SIZE)
) from table_b b where b.id_a = a.id
) format json,
'C_OBJECTS' VALUE (
select json_arrayagg(
JSON_OBJECT('ID' VALUE C.ID, 'SIZE' VALUE C.SIZE)
) from table_c c where c.id_a = a.id) format json
absent on null
)
from table_a a
gives
{
"ID": 1,
"NAME": "a",
"B_OBJECTS": [{
"ID": 1,
"SIZE": 5
}, {
"ID": 2,
"SIZE": 10
}, {
"ID": 3,
"SIZE": 15
}
],
"C_OBJECTS": [{
"ID": 1,
"SIZE": 100
}, {
"ID": 2,
"SIZE": 200
}
]
}

Related

Get all paths of matching jsonpath filter

I'm using PostgreSQL 14.
I need to find all paths that matches the jsonpath filter I give.
Input example
{
"A": [
{
"B": [
[
{
"name": "id",
"conditions": [
{
"validator": "nonnull"
}
]
},
{
"name": "x",
"conditions": [
{
"validator": "required"
}
]
},
{
"name": "y",
"rules": []
}
],
[
{
"name": "z",
"conditions": [
{
"validator": "required"
}
]
}
]
]
}
]
}
JsonPath filter
Every A.B which has a required validator condition:
$.A.B[*].conditions ? (#.validator == "required")
Expected output (Or something close)
{A,0,B,0,1,conditions,0}
{A,0,B,1,0,conditions,0}
This is a relational solution, you may define the query as a view and filter on the validator and you get the array indexes.
select a.index-1 as a,
b.index-1 as b,
b2.index-1 as b2,
c.item ->> 'validator' as validator,
c.index-1 as conditions
from t cross join
jsonb_array_elements(jsn -> 'A') WITH ORDINALITY a(item, index) cross join
jsonb_array_elements(a.item -> 'B') WITH ORDINALITY b(item, index) cross join
jsonb_array_elements(b.item) WITH ORDINALITY b2(item, index) cross join
jsonb_array_elements(b2.item -> 'conditions') WITH ORDINALITY c(item, index);
a|b|b2|validator|conditions|
-+-+--+---------+----------+
0|0| 0|nonnull | 0|
0|0| 1|required | 0|
0|1| 0|required | 0|

MongoDB partition window, getting the document where a field has the greatest value

I am translating some things from SQL into MongoDB.
I have a Mongo table set up where a document can contain lots of information. There are two ids, id_1 and id_2. id_2 has a default value of -1. There is a 1->n relationship between id_1 and id_2. For instance, the data can look like:
id_1 id_2 info
---- | ---- | ----
120 -1 'dont'
120 444 'show'
123 -1 'test'
124 -1 'hello'
125 -1 'world'
125 123 'oh wait'
126 -1 'help'
126 201 'me'
127 -1 'sql'
127 206 'hell'
I want to have a MongoDB query that gets the highest id_2 associated with an id_1.
Here is what the answer should look like given id_1 containing (123,124,125,126,127) and id_2 containing (-1,-1,123,201,206):
id_1 id_2 info
---- | ---- | ----
123 -1 'test'
124 -1 'hello'
125 123 'oh wait'
126 201 'me'
127 206 'hell'
In SQL this could be done using the following:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id_1 ORDER BY id_2 DESC) rn
FROM ids
WHERE id_1 IN (123, 124, 125, 126, 127) AND
id_2 IN (-1, -1, 123, 201, 206)
)
SELECT id_1, id_2, info
FROM cte
WHERE rn = 1;
In Mongo this can be done with a $group clause, however, it is very slow. See below:
{
'$sort' : {
'id_1': 1,
'id_2': 1
}
},
{
'$group' : {
'_id': '$id_1',
'id_1': {'$first': '$id_1'},
'info': { '$last': '$info'}
}
}
I found this in the documentation:
https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/#std-label-setWindowFields-window
However, I'm not getting good results. I think I'm misunderstanding window. Here is what I have:
{
'$match' : {
'id_1': {'$in' : [123,124,125,126,127]},
'id_2': {'$in' : [-1,-1,123,201,206]}
}
},
{
'$setWindowFields': {
'partitionBy': 'id_1',
'sortBy' : {
'id_2': -1
},
'output': {
'info': {
'$last': '$info'
},
}
}
},
{
'$project' : {
'id_1' : 1,
'id_2' : 1,
'info' : 1
}
}
This doesn't really seem to do anything except output every info for every combination of id_1 and id_2. Similarly, adding a range of [0,1] to a window in the output just results in an error that says:
Missing _sortExpr with range-based bounds
Does anyone know how to get the same results that I got in SQL?
$match match id_1 and id_2
$setWindowFields unbounded check the whole group by partition
$match keep only id_2 = max, means it's the largest document.
$unset remove max since it's unnecessary
db.collection.aggregate([
{
"$match": {
"id_1": { "$in": [ 123, 124, 125, 126, 127 ] },
"id_2": { "$in": [ -1, 123, 201, 206 ] }
}
},
{
$setWindowFields: {
partitionBy: "$id_1",
sortBy: { id_2: 1 },
output: {
max: {
$max: "$id_2",
window: {
documents: [ "unbounded", "unbounded" ]
}
}
}
}
},
{
"$match": {
$expr: { "$eq": [ "$id_2", "$max" ] }
}
},
{
"$unset": "max"
}
])
mongoplayground

Postgres jsonb to table

Is it possible to return jsonb array of arrays as a table object?
The size of the inner arrays will not always be the same.
Postgres version 9.6.6 is being used.
Example jsonb:
{
"r": [
{
"n": "name",
"rcs": [
[
{
"v": "1",
"vt": 9
},
{
"v": "2",
"vt": 9
},
{
"v": "3",
"vt": 9
}
],
[
{
"v": "4",
"vt": 9
},
{
"v": "5",
"vt": 7
}
]
]
}
]
}
Expected table
+------+------+--------+
| Col1 | Col2 | Col3 |
+------+------+--------+
| 1 | 2 | 3 |
| 4 | 5 | Null |
+------+------+--------+
The width of the returned table will be determined by the max length of all rows.
Excess columns for shorter rows will have null values.
I am completely new to Postgres, and not even sure where to start.
This was as far as I got:
select c from someTable,
jsonb_array_elements(data -> 'r') r,
jsonb_array_elements(r -> 'rcs') c
WHERE r->> 'n' = 'name'

How to port ARRAY_AGG with mulitple arguments in Postgresql to Snowflake

Snowflake supports ARRAY_AGG but it can take only parameter while Postgresql's version supports multiple.
I need to port the following snippet in Posgresql to Snowflake:
ARRAY_AGG(state, city, zipcode)
where state, city and zipcode are fields in one of my tables.
Any workaround? I know I can create 3 separate fields but that's not desired.
Depending if you are want an Array of Array's or an Array of Objects
WITH r AS (
SELECT column1 AS A, column2 AS B FROM (VALUES (1,'A'),(14,'B'),(35,'C'),(91,'D'),(105,'E'))
)
SELECT ARRAY_AGG(ARRAY_CONSTRUCT(a,b)) FROM r;
gives:
[ [ 1, "A" ], [ 14, "B" ], [ 35, "C" ], [ 91, "D" ], [ 105, "E" ] ]
or
WITH r AS (
SELECT column1 AS A, column2 AS B FROM (values (1,'A'),(14,'B'),(35,'C'),(91,'D'),(105,'E'))
)
SELECT ARRAY_AGG(OBJECT_CONSTRUCT('A',a,'B',b)) FROM r;
gives:
[ { "A": 1, "B": "A" }, { "A": 14, "B": "B" }, { "A": 35, "B": "C" }, { "A": 91, "B": "D" }, { "A": 105, "B": "E" } ]
https://docs.snowflake.net/manuals/sql-reference/functions/array_agg.html
https://docs.snowflake.net/manuals/sql-reference/functions/array_construct.html
https://docs.snowflake.net/manuals/sql-reference/functions/object_construct.html

mongo equivalent of sql query

i need to build a mongo query to get results from a collection which has the same structure as the following sql.
click for picture of table structure
my sql query:
SELECT * FROM (
SELECT
db.date,
db.points,
db.type,
db.name,
db.rank,
YEARWEEK( db.date ) AS year_week
FROM _MyDatabase db
WHERE
db.personId = 100 AND
db.date BETWEEN '2012-10-01' AND '2015-09-30'
ORDER BY
YEARWEEK( db.date ),
db.type,
db.points DESC
) x
GROUP BY
x.year_week DESC,
x.type;
the result looks like this
date points type name rank year_week
-------------------------------------------------
23.10.2014 2000 1 Fish 2 201442
12.10.2014 2500 1 Fish 2 201441
16.10.2014 800 2 Fish 2 201441
i have tried different group / aggregate queries so far, but i couldn't get a similar result. hopefully one of you has more mongo experience than i and can give me a hint on how to solve this.
You would want something like this:
var start = new Date(2012, 9, 1),
end = new Date(2015, 8, 30),
pipeline = [
{
"$match": {
"personId": 100,
"date": { "$gte": start, "$lte": end }
}
},
{
"$project": {
"date": 1, "points": 1, "type": 1, "name": 1, "rank": 1,
"year_week": { "$week": "$date" }
}
},
{
"$sort": {
"year_week": 1,
"type": 1,
"points": -1
}
},
{
"$group": {
"_id": {
"year_week": "$year_week",
"type": "$type"
}
}
}
];
db.getCollection("_MyDatabase").aggregate(pipeline);