Context
Using PostgreSQL, I’m storing articles and their associated events. Here is a simple structure of the 2 tables for the sake of the example:
Articles table
+----+-----------+-----------------------+ | ID | title | description | +----+-----------+-----------------------+ | 1 | article 1 | article 1 description | | 2 | article 2 | article 2 description | | 3 | article 3 | article 3 description | | 4 | article 4 | article 4 description | | 5 | article 5 | article 5 description | +----+-----------+-----------------------+
Events table
+-----+-----------+--------------+----------------+--------------+ | ID | name | eventable_id | eventable_type | created_at | +-----+-----------+--------------+----------------+--------------+ | 1 | validated | 1 | Article | 2020-05-10 | | 2 | reported | 1 | Article | 2020-05-11 | | 3 | reported | 2 | Article | 2020-05-10 | | 4 | reported | 2 | Article | 2020-05-11 | | 5 | reported | 2 | Article | 2020-05-12 | | 6 | reported | 3 | Article | 2020-05-20 | | 7 | validated | 3 | Article | 2020-05-21 | | 8 | reported | 4 | Article | 2020-05-12 | | 9 | moved | 4 | Article | 2020-05-13 | | 10 | reported | 4 | Article | 2020-05-14 | | 11 | moved | 5 | Article | 2020-05-13 | | 12 | moved | 5 | Article | 2020-05-14 | +-----+-----------+--------------+----------------+--------------+
Problem
Here I need to be able to get all articles which have their latest events being reported.
So for example, following the data above, we must only get:
article 1: because it has beenvalidatedthenreportedarticle 2: because it only has beenreportedarticle 4: because it has beenmovedthenreported(same as article 1)
As you can see:
article 3: should not be return because its latest event isvalidated.article 5: should not be return because its latest event ismoved.
I can easily find all articles having a reported event. But how to get the ones having their latest event to be reported ?
Here is what I did try to do so far without success:
SELECT *
FROM articles a
INNER JOIN (
SELECT *
FROM events
WHERE name = 'reported'
ORDER BY created_at
LIMIT 1
) AS e ON e.moderable_id = a.id AND e.moderable_type = 'Article'
We currently have:
- 459 892
articles - 62 074
events
Advertisement
Answer
You could use a correlated subquery for filtering:
select a.*
from articles a
where
(
select e.name
from events e
where e.eventable_id = a.id and e.eventable_type = 'Article'
order by created_at desc
limit 1
) = 'reported'
We could also express this with a lateral join:
select a.*
from articles a
inner join lateral (
select e.name
from events
where e.eventable_id = a.id and e.eventable_type = 'Article'
order by created_at desc
limit 1
) x on x.status = 'reported'