PostgreSQL WHERE Operators
Master the WHERE clause with operators in PostgreSQL. Learn comparison and logical operators, filtering techniques, and practical examples to refine your SQL queries.
Focus: use the where clause with operators
You’ve written a dozen queries that return whole tables, but now your real-world data is piling up—thousands of orders, millions of log lines—and you need only the rows that matter. Without a precise WHERE clause with operators, you’re left scrolling through noise, or worse, pulling too much data into memory and slowing your application. In this lesson, you’ll learn to filter data like a pro using PostgreSQL’s comparison and logical operators, so your queries become faster, safer, and more expressive.
The problem this lesson solves
Raw SELECT statements are blunt instruments: SELECT * FROM orders; returns every column of every row. When you’re debugging a production issue or building a dashboard, that’s not just unwieldy—it’s a performance hazard. The WHERE clause is your scalpel, letting you surgically extract only the rows that meet specific conditions. Without a solid grasp of operators, you’ll either under-filter (fetching too much) or over-filter (missing critical data). This lesson gives you the exact syntax and mental models to filter confidently.
Pro tip: Filtering early in your query reduces network transfer and memory usage—essential when your tables grow to millions of rows.
Core concept / mental model
Think of WHERE as a gatekeeper at a club. Each row in your table walks up to the door, and the gatekeeper checks the row against a bouncer’s checklist—your conditions. If the row passes, it’s let in; otherwise, it’s turned away. Operators are the rules of the checklist: “age is at least 21” (comparison), “VIP or guest” (logical OR), “not on the banned list” (negation).
Key terms to know:
- Comparison operators:
=,!=or<>,>,<,>=,<=— compare values. - Logical operators:
AND,OR,NOT— combine or negate conditions. - Pattern matching:
LIKE,ILIKE,SIMILAR TO— fuzzy matches with wildcards. - Range and membership:
BETWEEN,IN— shorthand for multiple comparisons.
The WHERE clause is evaluated after the FROM clause but before GROUP BY, HAVING, and ORDER BY. That means the filter happens early in the query pipeline, which is why it has such a big impact on performance.
How it works step by step
- Start with a base query:
SELECT column1, column2 FROM table_name; - Add the WHERE keyword: Place it immediately after the table name.
- Write a condition: Use an operator to compare a column (or expression) with a value or another column.
- Combine conditions: Use
AND,OR, andNOTto build complex logic. Remember,ANDbinds tighter thanOR, just like multiplication before addition in arithmetic. - Test and refine: Run the query, inspect the result set, and adjust. If you get too many rows, add more conditions; if too few, loosen them.
Operator precedence
PostgreSQL follows standard SQL precedence:
=,!=,<>,<,>,<=,>=NOTANDOR
So a OR b AND c means a OR (b AND c). Use parentheses to make the intent explicit and avoid bugs.
Hands-on walkthrough
Let’s apply this with a sample products table. First, create and populate it:
-- Create a simple products table
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
price NUMERIC(10, 2) NOT NULL,
category TEXT,
in_stock BOOLEAN DEFAULT true
);
-- Insert sample data
INSERT INTO products (name, price, category, in_stock) VALUES
('Widget A', 19.99, 'Tools', true),
('Widget B', 34.50, 'Tools', false),
('Gadget X', 12.00, 'Gadgets', true),
('Gadget Y', 99.99, 'Gadgets', true),
('Accessory Z', 5.75, 'Accessories', false);
Now, filter using comparison operators:
-- Find products that cost less than $20
SELECT name, price FROM products WHERE price < 20;
Output:
name | price
------------+-------
Widget A | 19.99
Gadget X | 12.00
Accessory Z| 5.75
Combine with logical operators:
-- Products that are in stock AND cost at least $10
SELECT name, price, in_stock
FROM products
WHERE in_stock = true AND price >= 10;
Output:
name | price | in_stock
----------+-------+----------
Widget A | 19.99 | t
Gadget X | 12.00 | t
Gadget Y | 99.99 | t
Use IN and BETWEEN for cleaner syntax:
-- Products in the Tools or Gadgets category with price between 10 and 50
SELECT name, category, price
FROM products
WHERE category IN ('Tools', 'Gadgets')
AND price BETWEEN 10 AND 50;
Output:
name | category | price
----------+----------+-------
Widget A | Tools | 19.99
Widget B | Tools | 34.50
Gadget X | Gadgets | 12.00
Compare options / when to choose what
| Operator | Use case | Example | When to prefer |
|---|---|---|---|
= / != |
Exact match | WHERE status = 'active' |
Quick, precise filtering on discrete values |
<, >, <=, >= |
Range comparisons | WHERE age >= 18 |
Numeric or date thresholds |
BETWEEN |
Inclusive range (shorthand) | WHERE price BETWEEN 10 AND 50 |
Clean, readable range tests |
IN |
Multiple discrete values | WHERE category IN ('A', 'B') |
Avoid long OR chains |
LIKE / ILIKE |
Pattern matching | WHERE name LIKE 'Widget%' |
Substring or prefix/suffix searches (ILIKE is case-insensitive) |
AND / OR / NOT |
Combine logic | WHERE active = true AND age > 21 |
Building complex filters |
When to choose what: Use = for exact matches, BETWEEN for inclusive numeric ranges, IN for small lists of known values, and ILIKE when case-insensitive text search is needed. For high-performance text search, consider full-text search (not covered here) instead of LIKE with leading wildcards.
Troubleshooting & edge cases
- NULL values:
WHERE column = NULLnever returns rows. UseIS NULLorIS NOT NULLinstead. For example,WHERE end_date IS NULLfinds open-ended records. - Precedence surprises:
WHERE active = true OR role = 'admin' AND age > 30is interpreted asactive = true OR (role = 'admin' AND age > 30), which might not be what you intended. Always parenthesizeAND/ORgroups. - BETWEEN is inclusive:
BETWEEN 10 AND 20includes 10 and 20. If you need exclusive bounds, use> 10 AND < 20. - Case sensitivity:
=is case-sensitive.WHERE name = 'widget'won’t match'Widget'. UseILIKEorlower()if needed (but beware performance implications). - Data type mismatches: Comparing a text column to a number can cause errors or implicit casts. Ensure types align.
What you learned & what's next
You now understand how to use the WHERE clause with operators to filter rows in PostgreSQL. You learned comparison operators for exact and range checks, logical operators to combine conditions, IN and BETWEEN for concise syntax, and you saw practical examples that you can run yourself. You also know how to avoid common pitfalls like NULL handling and precedence issues.
You’ve met the learning objectives: you can explain the core idea behind the WHERE clause with operators and complete a practical filtering exercise. Next in the track, you’ll build on this foundation by exploring sorting and limiting results with ORDER BY and LIMIT, which will help you control the shape and volume of your query output even further.
Practice recap
Try this: write a query on the products table that returns the names of products that are in stock and priced between $15 and $50, or that are in the 'Gadgets' category. Then modify it to exclude any product whose name starts with 'Gadget' using NOT LIKE. Run each version and compare the outputs — this will solidify your understanding of combining operators.
Common mistakes
- Using
= NULLinstead ofIS NULL— this returns no rows because NULL is not equal to anything. - Forgetting parentheses when mixing
ANDandOR— the result can be logically different from your intention. - Assuming
BETWEENis exclusive — it includes both endpoints, which can lead to off-by-one errors. - Making the query case-sensitive with
=when you really needILIKEfor text matching.
Variations
- Instead of
IN, you can use= ANY(ARRAY[...])— it behaves similarly and can be useful with subqueries. - For date ranges,
BETWEENis handy, but for open-ended ranges,>=and<withDATE_TRUNCorNOW()give more control. - Use
ILIKEfor case-insensitive pattern matching, which is an alternative to lowercasing columns or values withLOWER().
Real-world use cases
- Filtering e-commerce orders to show only those placed in the last 30 days using
WHERE order_date > NOW() - INTERVAL '30 days' - Finding all active users in a database who haven't logged in for over a week with
WHERE active = true AND last_login < NOW() - INTERVAL '7 days' - Extracting products in specific categories with a price range for a promotional campaign, using
WHERE category IN ('Electronics', 'Accessories') AND price BETWEEN 10 AND 100
Key takeaways
- The WHERE clause filters rows after the FROM clause and before grouping, making it the primary tool for narrowing result sets.
- Comparison operators (=, !=, <, >, <=, >=) handle exact and range matching; BETWEEN is a shorthand for inclusive ranges.
- Logical operators AND, OR, NOT combine conditions — remember that AND binds tighter than OR, and use parentheses to avoid ambiguity.
- IN and ILIKE provide concise alternatives to long OR chains and case-sensitive LIKE, respectively.
- NULL comparisons always evaluate to unknown; always use IS NULL or IS NOT NULL to check for NULL.
- Testing your query incrementally and checking the result set against your expectations prevents subtle filtering bugs.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.