If the maximum link depth is one level, then this simple query does the job:
select subcategory_id, name, count(*) from ( select distinct subcategory_id, coalesce(stack_id, id) stack_id from products ) sub join subcategories s on s.id = sub.subcategory_id group by 1, 2 order by 1, 2; subcategory_id | name | count
This recursive query also works correctly on links deeper than one level:
with recursive pr(id, subcategory_id, stack_id, stack) as ( select id, subcategory_id, stack_id, array[id] from products union select pr.id, pr.subcategory_id, products.stack_id, pr.stack_id || pr.stack from pr join products on pr.stack_id = products.id ) select distinct on (id) id, subcategory_id, stack from pr order by id, array_length(stack, 1) desc id | subcategory_id | stack
Attach the subcategories to the dataset above:
select subcategory_id, name, count(*) from ( select distinct subcategory_id, stack[1] from ( with recursive pr(id, subcategory_id, stack_id, stack) as ( select id, subcategory_id, stack_id, array[id] from products union select pr.id, pr.subcategory_id, products.stack_id, pr.stack_id || pr.stack from pr join products on pr.stack_id = products.id ) select distinct on (id) id, subcategory_id, stack from pr order by id, array_length(stack, 1) desc ) sub ) sub join subcategories s on s.id = sub.subcategory_id group by 1, 2 order by 1, 2 subcategory_id | name | count