Session 5 · Filtering rows with WHERE#
The big idea#
A table can have millions of rows and you usually want a few. WHERE keeps only the rows that
match a condition. You build conditions with comparisons (=, >), logic (AND, OR, NOT) and
helpers (IN, BETWEEN, LIKE, IS NULL). LIMIT then trims how many come back.
Everyday example: a shop’s search page. “Phones, under 20,000, brand Samsung or Redmi.” Each filter you tick throws away products until a short list is left. SQL filters do exactly this.
The queries#
All of these ran against the practice database. Read the SQL, then the output below it.
\echo '-- WHERE'
SELECT title, year FROM movies WHERE genre = 'Action';
\echo '-- AND'
SELECT title, year FROM movies WHERE genre = 'Action' AND year > 2000;
\echo '-- OR'
SELECT title, genre FROM movies WHERE genre = 'Crime' OR genre = 'Comedy';
\echo '-- IN'
SELECT title, year FROM movies WHERE year IN (1995, 2021);
\echo '-- BETWEEN is inclusive'
SELECT title, rating FROM movies WHERE rating BETWEEN 8.0 AND 8.5 ORDER BY rating;
\echo '-- LIKE and ILIKE'
SELECT title FROM movies WHERE title LIKE 'S%';
SELECT title FROM movies WHERE title ILIKE '%pottru';
\echo '-- = NULL never matches. IS NULL does'
SELECT title FROM movies WHERE genre = NULL;
SELECT title FROM movies WHERE genre IS NULL;
\echo '-- LIMIT and OFFSET, FETCH'
SELECT title, rating FROM movies WHERE rating IS NOT NULL ORDER BY rating DESC LIMIT 3;
SELECT title, rating FROM movies WHERE rating IS NOT NULL ORDER BY rating DESC OFFSET 3 FETCH FIRST 2 ROWS ONLY;
\echo '-- AND binds tighter than OR: parentheses change the answer'
SELECT title, genre, year FROM movies WHERE genre = 'Drama' OR genre = 'Action' AND year > 2015 ORDER BY year;
SELECT title, genre, year FROM movies WHERE (genre = 'Drama' OR genre = 'Action') AND year > 2015 ORDER BY year;
-- WHERE
title | year
--------+------
Baasha | 1995
Sivaji | 2007
Kaithi | 2019
Vikram | 2022
(4 rows)
-- AND
title | year
--------+------
Sivaji | 2007
Kaithi | 2019
Vikram | 2022
(3 rows)
-- OR
title | genre
---------+--------
Nayakan | Crime
Muthu | Comedy
(2 rows)
-- IN
title | year
--------+------
Baasha | 1995
Muthu | 1995
Karnan | 2021
(3 rows)
-- BETWEEN is inclusive
title | rating
------------+--------
Karnan | 8.0
Baasha | 8.2
Vikram | 8.3
Kaithi | 8.4
Thalapathi | 8.5
(5 rows)
-- LIKE and ILIKE
title
-----------------
Sivaji
Soorarai Pottru
(2 rows)
title
-----------------
Soorarai Pottru
(1 row)
-- = NULL never matches. IS NULL does
title
-------
(0 rows)
title
------------------
Untitled Project
(1 row)
-- LIMIT and OFFSET, FETCH
title | rating
-------------------+--------
Nayakan | 9.0
Pariyerum Perumal | 8.7
Soorarai Pottru | 8.6
(3 rows)
title | rating
------------+--------
Thalapathi | 8.5
Kaithi | 8.4
(2 rows)
-- AND binds tighter than OR: parentheses change the answer
title | genre | year
-------------------+--------+------
Thalapathi | Drama | 1991
Pariyerum Perumal | Drama | 2018
Kaithi | Action | 2019
Soorarai Pottru | Drama | 2020
Karnan | Drama | 2021
Vikram | Action | 2022
(6 rows)
title | genre | year
-------------------+--------+------
Pariyerum Perumal | Drama | 2018
Kaithi | Action | 2019
Soorarai Pottru | Drama | 2020
Karnan | Drama | 2021
Vikram | Action | 2022
(5 rows)
What to notice#
ANDnarrows,ORwidens. Four Action movies; addingyear > 2000leaves three.IN (1995, 2021)is shorthand foryear = 1995 OR year = 2021.BETWEEN 8.0 AND 8.5includes both ends. Karnan (8.0) and Thalapathi (8.5) are in the result.LIKE 'S%':%means “any characters”,_means exactly one.LIKEis case-sensitive;ILIKEis not, which is whyILIKE '%pottru'matched “Soorarai Pottru”.= NULLreturns nothing, even though a row has NULL.NULLmeans “unknown”, and “unknown equals unknown” is not true. UseIS NULL/IS NOT NULL.LIMIT 3gives the top three;OFFSET 3 FETCH FIRST 2 ROWS ONLYskips three and takes the next two (the standard SQL spelling ofLIMIT 2 OFFSET 3). Always pair them withORDER BY.ANDis evaluated beforeOR. The last two queries use the same words but return 6 and 5 rows. In the first, Thalapathi (a 1991 drama) sneaks in because it only had to satisfygenre = 'Drama'. Parentheses make your intent explicit.
A mental model for conditions#
Think of WHERE as a test the database runs on each row, one at a time. The row either passes
(the condition is true) and goes into the result, or it fails and is skipped. Three outcomes are
possible, not two: true, false, and unknown. A comparison with NULL is unknown, and WHERE
only keeps rows where the answer is true. That is the whole reason genre = NULL returns nothing
and why NOT IN misbehaves around NULL.
When a query gives surprising rows, test the pieces. Run SELECT title, genre, year FROM movies
first, look at the data, then add one condition at a time and watch how the row count shrinks (11,
then 4, then 3 in the diagram above). Building filters step by step is quicker than guessing.
Common mistakes#
WHERE genre = NULL. Always empty. UseIS NULL.Forgetting parentheses around mixed
AND/OR, as the last two queries show.LIKE '%word'on a huge table. A leading%cannot use a normal index and is slow.Using double quotes for text.
WHERE genre = "Action"fails; text values use single quotes.LIMITwithoutORDER BY. You get some rows, not the top ones.Using
NOT INwith a NULL in the list. The result is empty. PreferNOT EXISTS.
Hands-on exercises#
Exercise 1. Find movies from before 2000 that are not Drama.
Solution
SELECT title, genre, year FROM movies WHERE year < 2000 AND genre <> 'Drama' ORDER BY year;
Expected rows: Nayakan (Crime), Baasha (Action) and Muthu (Comedy).
Exercise 2. Find every movie whose title contains the letters ra anywhere, ignoring case.
Solution
SELECT title FROM movies WHERE title ILIKE '%ra%' ORDER BY title;
%ra% matches “ra” at any position. It returns two rows: Soorarai Pottru and Vikram.
Exercise 3. Show the third and fourth best rated movies.
Solution
SELECT title, rating FROM movies
WHERE rating IS NOT NULL
ORDER BY rating DESC
OFFSET 2 FETCH FIRST 2 ROWS ONLY;
Skip two rows, take two: Soorarai Pottru (8.6) and Thalapathi (8.5). The IS NOT NULL filter keeps the unrated movie out of the ranking.
Practice#
Take the Session 5 quiz. For more operators (SIMILAR TO, ESCAPE) see the reference pages
under Filtering Data.