I think this should be faster than the self join as only a single scan over the table is required:
select a,b
from (
select a,
b,
max(a) over (partition by b) as max_a
from the_table
where a < now()
)
where a < max_a;
If the condition a < now()
filters out many rows, then an index on (a,b)
will help. If that still leaves many rows, an index on (b,a)
might be the better choice to speed up finding the max a for a given. But only an execution plan on your real data will show that