Postgresql - Reducing rows to one if a condition is met - postgresql

I have a table that returns a series of objects. This is a small section of an example result (in JSON):
Query:
SELECT id, starttime, endtime, duration, type FROM things
Result:
{
"id": 3,
"starttime": "2016-09-15T03:27:09",
"endtime": "2016-09-15T03:31:43",
"duration": 274,
"type": "bad"
},
{
"id": 2,
"starttime": "2016-09-15T03:26:48",
"endtime": "2016-09-15T03:27:09",
"duration": 20,
"status": "good"
},
{
"id": 1,
"starttime": "2016-09-15T03:19:46",
"endtime": "2016-09-15T03:26:48",
"duration": 422,
"status": "bad"
},
I am trying to exclude anything less than 30 seconds - this is simple enough. However I also need to combine the top two together - their durations combined, the starttime with id 1's starttime and endttime as id 3's endtime. So this:
{
"starttime": "2016-09-15T03:19:46",
"endtime": "2016-09-15T03:31:43",
"duration": 696,
"status": "bad"
},
Is this possible in Postgres/SQL? I think I could figure something out in Java/C# but would rather do it in the query.

You can use ROW_NUMBER() :
SELECT MAX(CASE WHEN s.rnk = 1 THEN s.starttime END) as starttime,
MAX(CASE WHEN s.rnk = 2 THEN s.endtime END) as endtime,
SUM(s.duration) as dur,
--You didn't say which status you want
FROM (
SELECT t.*,
ROW_NUMBER() OVER(ORDER BY t.id) as rnk
FROM things t
WHERE t.duration >= 30) s
WHERE s.rnk < 3

Related

Jsonb array of objects update

So this is my jsonb array of objects. Column is called bids in my db.
bids column
[
{
"id": "1",
"size": "5.5Y",
"price": 180
},
{
"id": "f0d1d36a-f6af-409e-968e-54c1dc104566",
"size": "6.5Y",
"price": 22
}
]
I want to update price property by the ID of an element for ex. "f0d1d36a-f6af-409e-968e-54c1dc104566", so the price would change from 22 to 150 IN ROW WHICH CONTAINS ELEMENT WITH DESIRED ID IN THE COLUMN.
How can I do that?
create table json_update (id integer, json_fld jsonb);
insert into json_update values (1, '[
{
"id": "1",
"size": "5.5Y",
"price": 180
},
{
"id": "f0d1d36a-f6af-409e-968e-54c1dc104566",
"size": "6.5Y",
"price": 22
}
]'
)
;
UPDATE
json_update
SET
json_fld = jsonb_set(json_fld, ARRAY[(idx)::text, 'price'::text], '150'::jsonb)
FROM (
SELECT
(row_number() OVER (ORDER BY t.a ->> 'id') - 1) AS idx,
t.a
FROM (
SELECT
jsonb_array_elements(json_fld)
FROM
json_update) AS t (a)) AS i
WHERE
i.a ->> 'id' = 'f0d1d36a-f6af-409e-968e-54c1dc104566';
select * from json_update ;
id | json_fld
----+---------------------------------------------------------------------------------------------------------------------------
1 | [{"id": "1", "size": "5.5Y", "price": 180}, {"id": "f0d1d36a-f6af-409e-968e-54c1dc104566", "size": "6.5Y", "price": 150}]

How to correctly index jsonb arrays and the fields in array elements in postgreSQL

a have a simple table of purchases consisting of id and jsonb column, like this:
CREATE TABLE purchase (id SERIAL, products JSONB)
Then and index:
CREATE INDEX idx_purchase_products ON purchase USING GIN (products);
The sample data are like this:
INSERT INTO purchase VALUES (
1, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
}
]')
),
(
2, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
},
{
"country": 1,
"type": 2,
"size": 12,
"color": 4
},
{
"country": 2,
"type": 1,
"size": 12,
"color": 3
}
]')
),
(
3, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
}
]')
),
(
4, jsonb('[
{
"country": 1,
"type": 1,
"size": 10,
"color": 3
},
{
"country": 1,
"type": 2,
"size": 12,
"color": 4
},
{
"country": 2,
"type": 1,
"size": 12,
"color": 3
}
]')
);
And some scenarios of searching:
SELECT *
FROM purchase
WHERE products #> '[{"country": 1}]'
SELECT *
FROM purchase
WHERE products #> '[{"country": 1, "type": 1}]'
SELECT *
FROM purchase
WHERE products #> '[{"size": 12}]'
SELECT *
FROM purchase
WHERE products #> '[{"size": 12, "color": 4}]'
It is expected, that the customer could search for combinations:
country,
country + type
country + type + size
country + type + size + color
country + size
size + color
type + color
etc.
And there is a big chance, the list of 4 field (country, type, size, color) will grow in future to 7-10.
And of course we want also search combinations like this:
.. WHERE products #> '[{"country": 1}]' OR products #> '[{"color": 4}]' OR products #> '[{"type": 1, "size": 10}]'
Estimated size of the table purchase is 9-12 millions rows (depending on season).
Any idea how to implement the indexes to get the query result as fast as possible?

Can't sort by all array's items?

Postgresq 9.6
json
"availability": [
{
"qty": 25,
"price": 1599,
"is_available": true
},
{
"qty": 72,
"price": 3599,
},
"is_available": true
]
table with column data. Type is jsonb
If I want to sort first array's(availability) item by field "price" I this:
SELECT *
from product prod
WHERE to_tsvector('english', prod.data) ## to_tsquery('gram')
ORDER BY prod.data #> '{availability,0,price}' desc
OK.
But I need to sort all fields "price" in array availability
Smt like this (pseudo code)
SELECT *
from product prod
WHERE to_tsvector('english', prod.data) ## to_tsquery('gram')
ORDER BY prod.data #> '{availability,*,price}' desc
I need to to order by "price" desc.
The result must be
First record of result is second json
"availability": [
{
"qty": 25,
"price": 11599,
"is_available": true
},
{
"qty": 72,
"price": 13599,
},
"is_available": true
]
...
"availability": [
{
"qty": 25,
"price": 1599,
"is_available": true
},
{
"qty": 72,
"price": 3599,
},
"is_available": true
]
Is it possible?
This could be done like this:
select id,
jsonb_set(data, '{availability}',
(select jsonb_agg(item order by (item ->> 'price')::numeric)
from jsonb_array_elements(data -> 'availability') as x(item))
) as data
from product
where ...

How to get an associative array of rows from a subquery with postgres

I'm new to postgres and trying out some things before I take the leap over from mySQL.
What I'm trying to do is get an array of associative arrays into a single query.
It has to do with users that can select multiple contact types like phone, email and Facebook and I would like to retrieve those into the column 'contact'.
For a visualisation:
{
"first_name": "This",
"last_name": "is me",
"likes": [],
"city": null
}
And I would like to get something like this:
{
"first_name": "This",
"last_name": "Is me",
"likes": [],
"city": null,
"contact":
[
{"type":1, "value":"myemail#gmail.com", "privacy_rule":1},
{"type":4, "value":"myfacebook", "privacy_rule":1},
{"type":9, "value":"mylinkedin", "privacy_rule":1}
]
}
So the main query would be:
SELECT u.first_name, u.last_name, u.about, ARRAY(SELECT like_id FROM users_likes l WHERE l.user_id = u.user_id), u.city FROM users u WHERE user_id = {id}
The subquery would be:
SELECT c.type, c.value, c.privacy_rule FROM users_contact c WHERE c.user_id = u.user_id
But how do I integrate it in the main query to return the array of result rows?
Is it even possible?
Thanks in advance!
Ron
Ah, after some more filling about, here is the answer.
use json_build_object:
SELECT u.first_name, u.last_name,
ARRAY(SELECT like_id FROM users_likes l WHERE l.user_id = u.user_id) as likes,
ARRAY(SELECT json_build_object("contact_id", c.contact_id,
"value", c.value, "privacy",c.privacy)
FROM users_contact c WHERE c.user_id = u.user_id) as contact
FROM users_basic u WHERE user_id = {id}
This gives:
"first_name": "This",
"last_name": "Is Me",
"about": null,
"likes": [],
"city": null,
"contact": [
{
"contact_id": 1,
"value": "bbla",
"privacy": 2,
"type": "Phone"
},
{
"contact_id": 3,
"value": "blabla",
"privacy": 2,
"type": "Company Email"
},
{
"contact_id": 4,
"value": "blablabla",
"privacy": 2,
"type": "Telegram Id"
}
]
Hope it helps someone

datediff function for FQL, or other way to easily get event duration?

I want to return the event durations from this single facebook FQL query. Is there something like the SQL datediff function that I can use?
SELECT eid, name, start_time, end_time FROM event WHERE eid IN ( SELECT eid FROM event_member WHERE uid = me() ) ORDER BY start_time ASC
Best way to do this is definitely to post process as CBroe mentioned.
Here is an incomplete way to do this in FQL
SELECT start_time, end_time, strtotime(lower(end_time)) - strtotime(lower(start_time)) FROM event WHERE eid IN ( SELECT eid FROM event_member WHERE uid = me() ) AND strlen(end_time) > 0
And a sample response
{
"data": [
{
"start_time": "2015-03-14T21:00:00-0400",
"end_time": "2015-03-15T03:00:00-0400",
"anon": 21600
},
{
"start_time": "2015-02-16T21:00:00-0400",
"end_time": "2015-02-17T02:00:00-0400",
"anon": 18000
},
{
"start_time": "2015-01-30T22:00:00-0400",
"end_time": "2015-01-31T08:00:00-0400",
"anon": 36000
},
{
"start_time": "2015-01-24T18:00:00-0400",
"end_time": "2015-01-25T00:00:00-0400",
"anon": 21600
},
{
"start_time": "2015-01-09T22:00:00-0400",
"end_time": "2015-01-10T04:00:00-0400",
"anon": 21600
},
{
"start_time": "2015-01-02T22:00:00-0400",
"end_time": "2015-01-03T04:00:00-0400",
"anon": 21600
}
]
}
The anon field is returned in seconds so
21600 = 6 hours
18000 = 5 hours
36000 = 10 hours
Which correctly match the difference in start and end times above.