Reputation: 1653
I have table with following structure:
|id|author_id|name|type|created_at|updated_at
As a type I can have 5 different types, (A, B, C, D, E).
I need a query DB by author_id where I can select only last updated row "type" A and B. And select all other type rows.
So the result should be something like:
| id | author_id | name | type | created_at | updated_at
| 12 | 88 | lorem | A
| 45 | 88 | lorem | B
| 44 | 88 | lorem | C
| 154 | 88 | lorem | C
| 98 | 88 | lorem | C
| 856 | 88 | lorem | E
| 857 | 88 | lorem | E
Is it possible with single query? Or I need to use two queries?
Thank you
Upvotes: 2
Views: 893
Reputation: 1269773
I am thinking:
(select distinct on (type) t.*
from t
where type in ('A', 'B')
order by type, created_at desc
) union all
select t.*
from t
where type not in ('A', 'B');
In particular, this can make good use of an index on (type, created_at desc)
.
Upvotes: 0
Reputation: 222462
Assuming that id
is a unique key in the table, you could do this with distinct on
:
select distinct on(case when type in ('A', 'B') then type else id::text end) t.*
from mytable t
order by case when type in ('A', 'B') then type else id::text end, created_at desc, id
This uses a conditional expression as distinct on
key, that returns either type
if it is A or B, or the id
for other values. So you get the top 1 value for types A and B, and all other values for other types.
Upvotes: 0
Reputation: 521239
You may try the following:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY "type" ORDER BY updated_at DESC) rn
FROM yourTable
)
SELECT id, author_id, name, "type", created_at, updated_at
FROM cte
WHERE
("type" IN ('A', 'B') AND rn = 1) OR
"type" NOT IN ('A', 'B');
This approach uses ROW_NUMBER
to find the latest rows for all types. In the query on the CTE, we select only the most recently updated rows for types A
and B
, but we select all rows for all other types.
Upvotes: 2