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'