I have PostgreSQL 9.5 table(instrument) having 2 columns instrument_id and user_maps as shown below:
I want to fetch all instruments based on following conditions:
Loop through each json object in user_maps
count the number of json objects having status in Y, N or D
Count should be more than 2.
Note: user_maps is an array of json object having 2 fields status and userId
Sample user_maps linked to instrument_id "I01":
[
{
"status": "Y",
"userId": "ZU201707120539150007"
},
{
"status": "D",
"userId": "ZU201707120540510008"
},
{
"status": "I",
"userId": "ZU201707120542540009"
},
{
"status": "I",
"userId": "ZU201707011725050001"
},
{
"status": "Y",
"userId": "ZU201707120552050013"
}
]
Instrument id "I01" should come in final result .
Another, Sample user_maps linked to instrument_id "I02":
[
{
"status": "I",
"userId": "ZU201707120539150007"
},
{
"status": "I",
"userId": "ZU201707120540510008"
},
{
"status": "I",
"userId": "ZU201707120542540009"
},
{
"status": "I",
"userId": "ZU201707011725050001"
},
{
"status": "Y",
"userId": "ZU201707120552050013"
}
]
Instrument id "I02" should not come in final result beacuse it has only one json having status in (Y, N,D).
If I understood correctly your request then this is how you can do it:
-- This is just test dataset as you provided
WITH test( instrument_id, user_maps ) AS (
VALUES
( 'I01'::text,
$$[
{ "status": "Y", "userId": "ZU201707120539150007" },
{ "status": "D", "userId": "ZU201707120540510008" },
{ "status": "I", "userId": "ZU201707120542540009" },
{ "status": "I", "userId": "ZU201707011725050001" },
{ "status": "Y", "userId": "ZU201707120552050013" }
]$$::jsonb ),
( 'I02'::text,
$$[
{ "status": "I", "userId": "ZU201707120539150007" },
{ "status": "I", "userId": "ZU201707120540510008" },
{ "status": "I", "userId": "ZU201707120542540009" },
{ "status": "I", "userId": "ZU201707011725050001" },
{ "status": "Y", "userId": "ZU201707120552050013" }
]$$::jsonb )
)
SELECT t.instrument_id,
count( u )
FROM test t,
jsonb_array_elements( user_maps ) u -- does lateral join to get json array elements
WHERE u -> 'status' ?| ARRAY['Y', 'N', 'D'] -- your search condition
GROUP BY 1
HAVING count( u ) > 2; -- the count condition you wanted
-- This is the result of the query
instrument_id | count
---------------+-------
I01 | 3
(1 row)
Related
// orders collection
[
{
"id": 1,
"orderName": "a",
"seqId": 100,
"etc": [],
"desc": [],
},
{
"id": 2,
"orderName": "b",
"seqId": 200,
"etc": [],
"desc": []
},
{
"id": 3,
"orderName": "c",
"seqId": 100,
"etc": [],
"desc": [],
},
]
// goods collection
[
{
"id": 1,
"title": "example1",
"items": [
{
"id": 10,
"details": [
{
"id": 100
},
{
"id": 101,
}
]
},
{
"id": 20,
"details": [
{
"id": 102,
},
{
"id": 103,
}
]
},
]
},
[
{
"id": 2,
"title": "example2",
"items": [
{
"id": 30,
"details": [
{
"id": 200
},
{
"id": 201
}
]
},
{
"id": 40,
"details": [
{
"id": 202
},
{
"id": 203
}
]
},
]
},
]
When the value of the seqId field of the document whose etc field and desc field arrays of the orders collection are empty and the value of the "goods.details.id field of the goods collection are the same, I want to get the following output. How can I do that?
[
{orderName: "a", title: "example1"},
{orderName: "b", title: "example2"},
{orderName: "c", title: "example1"},
]
Additionally, I would like to perform a sum operation based on the title of the goods
collection.
[
{"example1": 2},
{"example2": 1}
]
Simply perform a $lookup between orders.seqId and goods.items.details.id. Use $unwind to eliminate empty lookups(i.e. inner join behaviour). Finally, do a $group with $sum to get the count.
db.orders.aggregate([
{
"$match": {
"etc": [],
"desc": []
}
},
{
"$lookup": {
"from": "goods",
"localField": "seqId",
"foreignField": "items.details.id",
"pipeline": [
{
$project: {
_id: 0,
title: 1
}
}
],
"as": "goodsLookup"
}
},
{
"$unwind": "$goodsLookup"
},
{
$group: {
_id: "$goodsLookup.title",
cnt: {
$sum: 1
}
}
}
])
Mongo Playground
Table public.challenge, column lines JSONB
My initial JSON in lines :
[
{
"line": 1,
"blocs": [
{
"size": 100,
"name": "abc"
},
{
"size": 100,
"name": "def"
},
{
"size": 100,
"name": "ghi"
}
]
},
{
"line": 2,
"blocs": [
{
"size": 100,
"name": "xyz"
}
]
}
]
Desired result (add a new object wrapper for every bloc) :
[
{
"line": 1,
"blocs": [
{
"size": 100,
"name": "abc",
"wrapper": {
"nestedName": "abc",
"type": "regular"
}
},
{
"size": 100,
"name": "def",
"wrapper": {
"nestedName": "def",
"type": "regular"
}
},
{
"size": 100,
"name": "ghi",
"wrapper": {
"nestedName": "ghi",
"type": "regular"
}
}
]
},
{
"line": 2,
"blocs": [
{
"size": 100,
"name": "xyz",
"wrapper": {
"nestedName": "xyz",
"type": "regular"
}
}
]
}
]
I have the following query (from here) :
WITH cte AS (
SELECT id_lines,
jsonb_agg(
jsonb_set(val1, '{blocs}',
(
SELECT jsonb_agg(arr2 ||
json_build_object(
'wrapper', json_build_object('nestedName', arr2->'name', 'type', 'regular')
)::jsonb
)
FROM jsonb_array_elements(arr1.val1->'blocs') arr2
WHERE arr2->'name' IS NOT NULL
)
))
FROM public.challenge, jsonb_array_elements(lines) arr1(val1)
GROUP BY 1
)
UPDATE public.challenge SET lines=(cte.jsonb_agg) FROM cte
WHERE public.challenge.id_lines=cte.id_lines;
The condition WHERE arr2->'name' IS NOT NULL does not filter out blocs where name is null, I struggle to find out why.. thanks!
You have to distinguish between SQL NULL and JSON null.
The IS NOT NULL predicate tests for SQL NULL, which would mean that the attribute is not present in the JSON.
To test for JSON null, use
WHERE arr2->'name' <> 'null'::jsonb
The type cast to jsonb is not necessary and would be performed implicitly.
Lets say I have a table and in a "data" NVARCHAR(MAX) column, I have this JSON:
[
{
"room": "kitchen",
"items": [
{
"name": "table",
"price": 100
}
]
},
{
"room": "bedroom",
"items": [
{
"name": "bed",
"price": 250
},
{
"name": "lamp",
"price": 50
}
]
},
{
"room": "bathroom",
"items": [
{
"name": "toilet",
"price": 101
},
{
"name": "shower",
"items": [
{
"name": "shower curtain",
"price": 10
},
{
"name": "shower head",
"price": 40
}
]
}
]
}
]
Using TSQL, can I somehow SUM all prices in the JSON? Please notice that my "price" is in different levels in the JSON file.
And furthermore, can I make a computed column that will SUM all the prices in the JSON column?
In JSon you would have to treat all nodes and check if another sublevel including "Price" exists. As text it is easier. This example is for one cell.
The idea above to insert the result to another column in the table is a good one.
You can implement a trigger after every INSERT / UPDATE to calculate instead of a computed column.
declare #str varchar(4000)='[ { "room": "kitchen", "items": [ { "name": "table", "price": 100 } ] }, { "room": "bedroom", "items": [ { "name": "bed", "price": 250 }, { "name": "lamp", "price": 50 } ] }, { "room": "bathroom", "items": [ { "name": "toilet", "price": 101 }, { "name": "shower", "items": [ { "name": "shower curtain", "price": 10 }, { "name": "shower head", "price": 40 } ] } ] } ]'
, #sub varchar(15);
drop table if exists #prices;
create table #prices(price int);
WHILE patindex('%"price": %',#str) > 0
begin
SELECT #sub=SUBSTRING(#str, patindex('%"price": %',#str), 15)
WHILE PatIndex('%[^0-9]%', #sub) > 0
SET #sub = Stuff(#sub, PatIndex('%[^0-9]%', #sub), 1, '');
insert into #prices select try_cast(#sub as int);
SET #str = Stuff(#str, patindex('%"price": %',#str), 15, '');
end;
select sum(price) from #prices;
Sample Doc :
{
"id": "K",
"powers": [
{
"label": "a",
"Rating": 7
},
{
"label": "b",
"Rating": 3
},
{
"label": "c",
"Rating": 4
},
{
"label": "d",
"Rating": 5
}
],
"phy": {
"height": 67,
"weight": 150
}
}
For this collection, I want to count how many distinct powers each id has.
I want the result as - ID =K, distinct power label - 4
So the easiest way to get it done is
/** db.collection.distinct('field on which distinct is needed', condition) */
db.collection.distinct('powers.label', {"id" : "K"})
As it will be an array, You can do .length in code to get the unique length.
Ref : .distinct()
With the following JSON I want to get the max value of the email ID.
[
{
"id": 1,
"firstName": "Bill",
"lastName": "Smith",
"emails": [
{
"id": 1,
"email": "jack#jackkkfgfkfgkkkkkk.com",
"dateCreated": "2017-05-14T14:04:12.3299297-07:00"
},
{
"id": 2,
"email": "jack#jackkkfkkkkkk.com",
"dateCreated": "2017-05-14T14:04:24.1534621-07:00"
},
{
"id": 3,
"email": "jack#jackfkkkkkk.com",
"dateCreated": "2017-05-14T14:04:29.8526171-07:00"
}
]
},
{
"id": 2,
"firstName": "Bill",
"lastName": "Smith",
"emails": [
{
"id": 4,
"email": "jack#jkkkkk.com",
"dateCreated": "2017-05-14T14:04:45.8674213-07:00"
}
]
}
]
My code so far
var maxId = People.Select(p => p.Emails.Max(i => i.Id)).FirstOrDefault();
This however always returns 4, because it only check the first element. Is there a way to check all in a single linq statement? So the next value would be 5.
You want the Max Id of all the emails.
var maxId = People.SelectMany(p => p.Emails).Max(e => e.Id);
var nextId = maxId++;
Use SelectMany to flatten all the emails into one sequence and then get the Max Id from that list.