PostgreSQL In and Between

IN and BETWEEN let you filter rows against a list of values or an inclusive range without stacking multiple OR conditions.

The IN operator

IN checks whether a column's value matches any value in a given list. It is a shorthand for writing a long chain of OR conditions, and it is both more readable and typically faster for PostgreSQL to plan.

Orders in specific statuses

SELECT *
FROM orders
WHERE status IN ('pending', 'shipped', 'delivered');

You can negate the check with NOT IN to exclude a list of values instead of including them.

Exclude cancelled and refunded orders

SELECT *
FROM orders
WHERE status NOT IN ('cancelled', 'refunded');

The BETWEEN operator

BETWEEN tests whether a value falls within an inclusive range, meaning both the lower and upper bounds are included in the match. It works with numbers, dates, and even text.

Products in a price range

SELECT name, price
FROM products
WHERE price BETWEEN 20 AND 50;
  • IN(...) checks membership in a fixed list of values
  • NOT IN(...) excludes rows matching any value in the list
  • BETWEEN low AND high is inclusive on both ends
  • NOT BETWEEN finds values outside the given range
  • IN can also be used with the result of a subquery

Combining with subqueries

IN becomes especially powerful when the list comes from another query instead of a hardcoded set of literals.

Customers who have placed an order

SELECT name
FROM customers
WHERE id IN (SELECT customer_id FROM orders);
OperatorEquivalent long form
status IN ('a','b')status = 'a' OR status = 'b'
price BETWEEN 20 AND 50price >= 20 AND price <= 50
status NOT IN ('a','b')status <> 'a' AND status <> 'b'
Note: BETWEEN dates can be tricky with timestamps: 'BETWEEN 2024-01-01 AND 2024-01-31' excludes times later than midnight on the 31st. Use an explicit upper bound of the next day if the column stores time-of-day values.
Note: NOT IN behaves unexpectedly if the list contains a NULL value, because comparing anything to NULL yields unknown rather than true or false, which can silently return zero rows.

Exercise: PostgreSQL In and Between

What does the BETWEEN operator include when checking a range?