How to optimize min/max queries for large tables on PostgreSQL
With only a few dozen distinct values of source_id, you can get fast execution on the index you built, using a loose index scan, aka a "skip scan".  Unfortunately PostgreSQL does not plan those automatically, so you have to force it into one, by using a recursive query.

with recursive t as ( 
   select min(source_id) as col from mydata 
   union all 
   select (select min(source_id) from mydata where source_id&gt;t.col)
      from t where t.col is not null) 
select 
  col, 
  (select min(last_updated_date) from mydata where source_id=col),
  (select max(last_updated_date) from mydata where source_id=col)
  from t;


Even if you don't resort to this, just doing the query as you originally wrote it should not take nearly an hour.  But without seeing an explain and an explain analyze, there isn't much more that can be said on that.