FILTER says what the CASE spelling implies: 'this aggregate, over this subset.' The equivalence is exact for SUM/MIN/MAX; for COUNT it's cleaner than CASE (no missing-ELSE reasoning). The portability tax is the dialect half: Postgres/DuckDB/SQLite yes; BigQuery/SQL Server/MySQL no (CASE is the travel kit). Pick one spelling per codebase; mixed reports make reviewers re-verify equivalence line by line.
SELECT otter, SUM( CASE WHEN kind = 'fish' THEN g END ) AS fish FROM feed GROUP BY otter;
SELECT otter, SUM(g) FILTER( WHERE kind = 'fish' ) AS fish FROM feed GROUP BY otter;
Both spellings give the same fish grams — FILTER (WHERE …) wears the condition on the outside of the aggregate, identical semantics to the CASE form but clearer, and narrower in portability (Postgres/DuckDB yes; BigQuery/MySQL no).
FILTER where it lives, CASE as the portable expansion; one spelling per codebase.
FILTER (WHERE …) in Postgres/DuckDB/SQLite; expand to CASE on BigQuery/SQL Server/MySQL.