4 WHERE
WHERE is how you filter rows. The clause sits between FROM and ORDER BY, and PostgreSQL keeps only rows for which the condition evaluates to true. Anything that evaluates to false or NULL is dropped — this three-valued logic is one of SQL’s quirks and the cause of many beginner bugs. Get comfortable with it early.
What you’ll learn
- Filtering with comparison operators (
=,<>,<,>,<=,>=) - Pattern matching with
LIKE(andILIKEfor case-insensitive) - Combining conditions with
AND,OR,NOT - Membership and ranges:
IN,BETWEEN - Why
= NULLdoes not work and what to use instead
Example
LIKE is the SQL standard pattern operator: % matches any string of characters (including empty), _ matches exactly one character. ILIKE is a PostgreSQL extension for case-insensitive matching. Index use depends on the pattern, collation, and operator class. Alphabetic prefixes generally need a suitable expression or trigram index; a B-tree can support some patterns beginning with characters unaffected by case. For more powerful matching, PostgreSQL also supports ~ (regex) and ~* (case-insensitive regex), as well as full-text search (covered later).
AND binds tighter than OR, so A AND B OR C means (A AND B) OR C. When in doubt, add parentheses — your future self reading the query will thank you. IN (...) is shorthand for = ANY (ARRAY[...]) and is equivalent to a chain of ORs.
Run the example
To run:
Common pitfalls
= NULLand<> NULLare never true (they areNULL). UseIS NULLandIS NOT NULL. This trips up engineers from every other language.NOT IN (subquery)becomes mysterious if the subquery contains aNULL— nonmatching values yieldNULLwhile matching values yield false, so the filter keeps no rows. PreferNOT EXISTSfor nullable subqueries.LIKE 'abc'does not match'ABC'. UseILIKEor applyLOWER()to both sides — butLOWER(col) = 'abc'defeats a plain index oncol(add an expression index onLOWER(col)if you need it often).- Be careful with
IN ('a', 'b', NULL)— theNULLin the list contributes nothing useful and silently makesNOT INbehave unexpectedly.
When filtering text by prefix, WHERE name LIKE 'foo%' can use a B-tree index with a suitable collation or text_pattern_ops operator class. WHERE name LIKE '%foo' (suffix) or '%foo%' (substring) cannot — consider a trigram index (pg_trgm extension) for those.
Change the condition to WHERE schemaname = 'public' — if you have no tables in public yet, you will get zero rows. Then try WHERE tablename ~ '^pg_a.*' to see regex matching.
Downloads
SQL file: where.sql · All lesson files · Setup guide