If your pav_type values ββare sequential, you can use generate_series instead of an external table:
select p.pav_type, count(yes) from generate_series(0,3) as p(pav_type) left outer join shelter_inventory s on p.pav_type = s.pav_type and s.yes = 1 group by p.pav_type order by p.pav_type
This gives:
pav_type | count ----------+------- 0 | 1 1 | 2 2 | 1 3 | 0
It:
generate_series(0,3) as p(pav_type)
essentially creates an inline table with one column named pav_type and four rows: 0, 1, 2, 3. And you need to have s.yes = 1 in the join condition (and not in WHERE), because you want the values yes = 0 which should be in a pre-grouped result set; if s.yes = 1 is in the WHERE clause, then yes = 0 lines will not be taken into account regardless of the connection conditions.
If your pav_types not suitable for generate_series (i.e. not sequential or phased), and you only have a small number of them, you can join the VALUES expression :
select p.pav_type, count(yes) from (values (0), (1), (2), (3)) as p(pav_type) left outer join shelter_inventory s on p.pav_type = s.pav_type and s.yes = 1 group by p.pav_type order by p.pav_type
You need to make sure that you, of course, find all brackets in the right places.
If you have pav_types in a separate table, then use the LEFT OUTER JOIN for this table instead of using generate_series . If your pav_types not sequential, and you have too much to reasonably enter a VALUES expression, then create a table to store the valid pav_type and LEFT OUTER JOIN values.