<< Home

Chapter 12: Filtering Results

Filtering limits query results to only the rows that meet specified conditions. The WHERE clause forms the backbone of filtering, combining comparison operators, ranges, pattern matching, and logical connectors to express precise criteria. This chapter explores essential filters, logical combinations, and how operator precedence affects evaluation.

šŸ’” Filtering early reduces work. The database engine applies WHERE conditions before grouping and sorting, which can save significant processing time.

WHERE, BETWEEN, LIKE, IN, IS NULL

The WHERE clause specifies a condition for each row. Only rows evaluating to true are included in the result set.

Basic comparisons

SELECT * 
FROM employees
WHERE department = 'Sales'
  AND salary > 60000;

Use single quotes for string literals and avoid implicit conversions between text and numbers.

Using BETWEEN for ranges

SELECT id, total
FROM orders
WHERE total BETWEEN 100 AND 500;

BETWEEN includes both boundary values. For exclusive ranges, use explicit comparisons.

WHERE total > 100 AND total < 500;
āš ļø For dates, ensure consistent data types and time zones. Using BETWEEN on timestamps can include unintended results if time components differ.

Pattern matching with LIKE

LIKE uses wildcards to match string patterns:

SELECT first_name, last_name
FROM employees
WHERE last_name LIKE 'Sm%';     -- starts with Sm

SELECT * 
FROM customers
WHERE email LIKE '%@example.com';  -- ends with @example.com
šŸ’” For case-insensitive matching, use ILIKE (PostgreSQL) or apply LOWER() to both sides. Create functional indexes on LOWER(column) for performance.

Matching sets with IN

SELECT * 
FROM products
WHERE category IN ('Electronics', 'Books', 'Home');

IN checks membership in a list of values or subquery results:

SELECT id, name
FROM employees
WHERE department_id IN (
  SELECT id FROM departments WHERE region = 'EU'
);
āš ļø NOT IN behaves differently when subqueries return NULL values. Prefer NOT EXISTS to avoid unexpected filtering.

Checking for NULL values

NULL represents missing or unknown data. You must use IS NULL or IS NOT NULL to test it.

SELECT id, email
FROM customers
WHERE email IS NULL;

SELECT id, email
FROM customers
WHERE email IS NOT NULL;
šŸ’” Comparisons with NULL using = or <> always return false. Handle missing values explicitly with IS NULL or COALESCE().

Logical operators

Logical operators combine multiple conditions in a WHERE clause. They control how filters interact to include or exclude rows.

AND and OR

SELECT * 
FROM employees
WHERE department = 'Engineering'
  AND salary > 90000;

All conditions joined by AND must be true for a row to qualify. OR allows any one of its conditions to be true.

SELECT * 
FROM employees
WHERE department = 'Sales'
   OR department = 'Marketing';

You can mix them, but operator precedence determines evaluation order (see below). Parentheses clarify intent and prevent logical errors.

NOT operator

NOT negates a condition. It can be used with any expression that evaluates to true or false.

SELECT * 
FROM employees
WHERE NOT (department = 'HR' OR salary < 50000);
āš ļø NOT IN and NOT EXISTS differ in how they treat nulls. If the subquery result can include NULL, NOT IN may return no rows at all.

Combining filters logically

SELECT * 
FROM orders
WHERE (status = 'SHIPPED' OR status = 'DELIVERED')
  AND total > 100;

Always test combinations carefully. A missing parenthesis or inverted logic can drastically change results.

šŸ’” Read combined filters aloud in plain language ("status is shipped or delivered, and total is over 100") to catch logical mistakes before execution.

Operator precedence and parentheses

When combining multiple conditions, SQL follows a fixed order of evaluation:

Order Operator Description
1 NOT Negation
2 AND Conjunction (both must be true)
3 OR Disjunction (either condition true)

Without parentheses, AND conditions are evaluated before OR. This means:

-- This query returns rows where
-- (department = 'Sales' AND salary > 50000) OR department = 'HR'
SELECT * 
FROM employees
WHERE department = 'Sales' AND salary > 50000
   OR department = 'HR';

To make intent explicit, group conditions with parentheses:

SELECT * 
FROM employees
WHERE department = 'HR'
   OR (department = 'Sales' AND salary > 50000);
āš ļø Even seasoned developers misread logical precedence when scanning long WHERE clauses. Always use parentheses for clarity instead of relying on default order.

Mastering filters and logical combinations makes queries accurate, readable, and efficient. By understanding how SQL evaluates conditions, you can write powerful searches that return exactly the intended data.

šŸ’” Think of parentheses as punctuation for logic. Use them liberally to match human reading order, not just machine evaluation.