# SQL WHERE: Filter Rows by a Condition

- URL: https://betteranalyst.com/sql/where

SQL guide · Filtering

The `WHERE` clause limits which rows a statement works on. After the database has located the rows in the `FROM` table, it evaluates the condition in `WHERE` for each row and keeps only the rows for which the condition is true. Every other row is discarded before grouping, sorting or returning results.

`WHERE` is not limited to `SELECT`. The same clause decides which rows an `UPDATE` changes and which rows a `DELETE` removes, which makes it the single most important safety control in data-changing statements. A missing `WHERE` on an `UPDATE` rewrites the whole table.

Conditions are built from comparison operators (`=`, `<>`, `<`, `>`, `<=`, `>=`), and can be combined with `AND`, `OR` and `NOT`, or written with helpers such as `IN`, `BETWEEN`, `LIKE` and `IS NULL`. Columns used in `WHERE` are the usual candidates for an index, so a well written condition is also the main lever for query speed.

## Basic syntax

```
SELECT column1, column2
FROM table_name
WHERE condition;

UPDATE table_name SET column1 = value WHERE condition;
DELETE FROM table_name WHERE condition;
```

The condition is any expression that evaluates to true, false or unknown. Only rows where it is true are kept, so a comparison against NULL (which yields unknown) removes the row. Aggregates such as `SUM()` cannot appear in `WHERE`; use `HAVING` for conditions on grouped results.

## SQL WHERE examples

### Filter on a single value

The simplest condition compares a column to a literal. Text and date literals go in single quotes, numbers do not. This returns only the customers located in Germany.

```
SELECT customer_id, name, city
FROM customers
WHERE country = 'Germany';
```

### Combine several conditions

Use `AND` when every condition must hold and `OR` when any one is enough. Parentheses make the grouping explicit, which matters because `AND` is evaluated before `OR`. Here we want recent orders that are either large or flagged as priority.

```
SELECT order_id, customer_id, total, order_date
FROM orders
WHERE order_date >= '2026-01-01'
  AND (total > 500 OR is_priority = 1);
```

### Use WHERE in an UPDATE

The clause controls which rows change. This statement applies a price increase only to products in one category and leaves everything else alone. Running the matching `SELECT` first is a cheap way to preview the affected rows.

```
UPDATE products
SET price = price * 1.05
WHERE category = 'Accessories';
```

### Filter with a subquery

The right side of a comparison can come from another query. This returns employees paid more than the company average. The subquery runs once and returns a single value.

```
SELECT employee_id, name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
```

## Where SQL dialects differ

**PostgreSQL:** String comparisons with `=` are case sensitive, and `LIKE` is too. Use `ILIKE` for case-insensitive matching. Boolean columns can be used directly, as in `WHERE is_active`.

**MySQL:** With the default collations (such as `utf8mb4_0900_ai_ci`, the default since 8.0) string comparison is case insensitive, so `WHERE name = 'smith'` also matches `Smith`. Double quotes may be read as string literals unless `ANSI_QUOTES` is set.

**SQL Server:** Case sensitivity depends on the database collation, and the default collations are case insensitive. There is no boolean type, so write `WHERE is_active = 1` rather than `WHERE is_active`.

**SQLite:** Comparisons use the column type affinity and are case sensitive for `=` by default, while `LIKE` is case insensitive for ASCII. Booleans are stored as 0 and 1.

## Common mistakes

- Forgetting `WHERE` on `UPDATE` or `DELETE`, which affects every row. Always write and test the filter as a `SELECT` first.
- Testing for NULL with `= NULL`. The result is unknown, never true, so no rows match. Use `IS NULL` instead.
- Mixing `AND` and `OR` without parentheses. `AND` binds tighter, so `a OR b AND c` means `a OR (b AND c)`, which is often not what was intended.
- Putting an aggregate such as `COUNT(*) > 5` in `WHERE`. Aggregates are computed after filtering, so the condition belongs in `HAVING`.
- Wrapping an indexed column in a function, as in `WHERE YEAR(order_date) = 2026`. This usually prevents index use; compare the raw column against a date range instead.

## Related SQL guides

- [SQL AND](https://betteranalyst.com/sql/and)
- [SQL OR](https://betteranalyst.com/sql/or)
- [SQL IN](https://betteranalyst.com/sql/in)
- [SQL BETWEEN](https://betteranalyst.com/sql/between)
- [SQL IS NULL](https://betteranalyst.com/sql/is-null)
- [SQL HAVING](https://betteranalyst.com/sql/having)

[Browse all SQL guides](https://betteranalyst.com/sql)

## Need help writing the SQL?

Describe your tables and the result you want, then review the generated query against your database before running it.

[Try the AI SQL generator](https://betteranalyst.com/ai-sql-query-generator)
