NULL = anything is UNKNOWN, so a NULL key never satisfies the ON condition — the row simply does not participate. Nothing errors; the count is just smaller than the floor. The syntax (LEFT JOIN, COALESCE buckets) is the easy half; the real question is what the business wants done with the unmatched — an 'Unassigned' bucket, a separate count, or genuine exclusion — chosen, and written down.
SELECT c.cart, z.zoneFROM carts c JOIN zones z ON z.zone_id = c.zone_id ORDER BY c.cart;
SELECT c.cart, COALESCE(z.zone, 'Unassigned') AS zone FROM carts c LEFT JOIN zones z ON z.zone_id = c.zone_id ORDER BY c.cart;
Two carts with a NULL zone quietly dropped from the compliance count — a NULL join key is never equal to anything, so the INNER join skips those rows.
LEFT JOIN with an explicit COALESCE bucket, a companion unmatched-count query, or a deliberate INNER JOIN with a comment saying so.