SQL IN and BETWEEN Operators
IN checks whether a value is in a list, and BETWEEN checks whether it lies in a range. Both are shorter and clearer than chains of OR and AND, and both hide one surprise each: BETWEEN includes both ends, and NOT IN breaks when the list contains NULL.
- Match a list of values with
INandNOT IN - Filter number and date ranges with
BETWEEN - Remember that
BETWEENis inclusive - Avoid the
NOT IN+ NULL trap
Sample data
| order_id | customer_id | order_date | status |
|---|---|---|---|
| 1001 | 1 | 2025-01-05 | Delivered |
| 1002 | 2 | 2025-01-17 | Delivered |
| 1003 | 1 | 2025-02-02 | Delivered |
| 1004 | 3 | 2025-02-14 | Cancelled |
| 1005 | 4 | 2025-03-08 | Delivered |
| 1006 | 5 | 2025-03-22 | Shipped |
| 1007 | 2 | 2025-04-10 | Delivered |
| 1008 | 1 | 2025-04-28 | Pending |
| 1009 | 3 | 2025-05-15 | Delivered |
| 1010 | 5 | 2025-06-01 | Delivered |
IN: match any value in a list
Orders from customers 1, 3 and 5:
SELECT order_id, customer_id, statusFROM ordersWHERE customer_id IN (1, 3, 5);| order_id | customer_id | status |
|---|---|---|
| 1001 | 1 | Delivered |
| 1003 | 1 | Delivered |
| 1008 | 1 | Pending |
| 1004 | 3 | Cancelled |
| 1009 | 3 | Delivered |
| 1006 | 5 | Shipped |
| 1010 | 5 | Delivered |
- For each order, MySQL checks whether
customer_idequals any value in the list. - It is the same as
customer_id = 1 OR customer_id = 3 OR customer_id = 5, but shorter. - Customer 1 has three orders, customer 3 two and customer 5 two: 7 rows.
IN works with text too:
SELECT order_id, statusFROM ordersWHERE status IN ('Pending', 'Shipped');| order_id | status |
|---|---|
| 1006 | Shipped |
| 1008 | Pending |
NOT IN
SELECT product_name, categoryFROM productsWHERE category NOT IN ('Electronics', 'Stationery');| product_name | category |
|---|---|
| Office Chair | Furniture |
| Standing Desk | Furniture |
| Desk Lamp | Furniture |
| Monitor Arm | Furniture |
NOT INkeeps rows whose value matches none of the list.- Only Furniture remains: 4 products.
IN with a subquery
The list can come from another query. Customers who have at least one order:
SELECT customer_id, customer_nameFROM customersWHERE customer_id IN (SELECT customer_id FROM orders);| customer_id | customer_name |
|---|---|
| 1 | Ananya Sharma |
| 2 | Rohit Kumar |
| 3 | Fatima Khan |
| 4 | David Fernandes |
| 5 | Ishita Bose |
- The inner query returns the
customer_idof every order (with repeats). - The outer query keeps customers whose id is in that list: 5 of the 7 customers.
You will learn more in Subqueries.
The NOT IN and NULL trap
If the list contains a NULL, NOT IN returns no rows at all:
SELECT first_nameFROM employeesWHERE emp_id NOT IN (SELECT manager_id FROM employees);- The subquery returns every
manager_id, including the CEO’sNULL. - For an employee such as 103, MySQL checks
103 <> 101 AND 103 <> 100 AND ... AND 103 <> NULL. 103 <> NULLis UNKNOWN, so the wholeANDchain is never TRUE: Empty set.
Exclude the NULLs and the query finds the employees who manage nobody:
SELECT first_nameFROM employeesWHERE emp_id NOT IN (SELECT manager_id FROM employees WHERE manager_id IS NOT NULL);| first_name |
|---|
| Priya |
| Karan |
| Neha |
| Arjun |
| Rohan |
| Sneha |
| Amit |
Prefer NOT EXISTS for “rows with no match in another table”. It handles NULLs the way you expect.
BETWEEN: a range
Products priced from 1,000 to 4,000:
SELECT product_name, priceFROM productsWHERE price BETWEEN 1000 AND 4000ORDER BY price;| product_name | price |
|---|---|
| Desk Lamp | 1499 |
| USB-C Hub | 1999 |
| Monitor Arm | 2799 |
| Mechanical Keyboard | 3499 |
BETWEEN 1000 AND 4000meansprice >= 1000 AND price <= 4000.- Both ends are included. A product priced exactly 1,000 or 4,000 would match.
- Four products fall in the range.
BETWEEN with dates
Orders placed in February and March 2025:
SELECT order_id, order_dateFROM ordersWHERE order_date BETWEEN '2025-02-01' AND '2025-03-31';| order_id | order_date |
|---|---|
| 1003 | 2025-02-02 |
| 1004 | 2025-02-14 |
| 1005 | 2025-03-08 |
| 1006 | 2025-03-22 |
For DATETIME columns, BETWEEN '2025-02-01' AND '2025-03-31' stops at midnight at the start of 31 March and misses the rest of that day. Use a half-open range instead: order_time >= '2025-02-01' AND order_time < '2025-04-01'.
NOT BETWEEN
SELECT first_name, salaryFROM employeesWHERE salary NOT BETWEEN 50000 AND 100000ORDER BY salary;| first_name | salary |
|---|---|
| Amit | 25000 |
| Rohan | 45000 |
| Arjun | 48000 |
| Vikram | 105000 |
| Asha | 125000 |
| Meera | 250000 |
NOT BETWEEN 50000 AND 100000keeps salaries below 50,000 or above 100,000.- Three people earn less than 50,000 and three earn more than 100,000.
BETWEEN with text
BETWEEN also works on text, comparing alphabetically:
SELECT customer_nameFROM customersWHERE customer_name BETWEEN 'A' AND 'G'ORDER BY customer_name;| customer_name |
|---|
| Ananya Sharma |
| David Fernandes |
| Fatima Khan |
Note that a name like 'Gaurav' would not match, because 'Gaurav' sorts after 'G'. To include every name starting with G, use customer_name < 'H'.
Exercise: Mid-range employees
List employees whose salary is from 45,000 to 75,000, highest first.
| first_name | salary |
|---|---|
| Priya | 72000 |
| Karan | 72000 |
| Neha | 54000 |
| Arjun | 48000 |
| Rohan | 45000 |
Hint 1
BETWEEN 45000 AND 75000 includes both ends.
SELECT first_name, salaryFROM employeesWHERE salary BETWEEN 45000 AND 75000ORDER BY salary DESC;| first_name | salary |
|---|---|
| Priya | 72000 |
| Karan | 72000 |
| Neha | 54000 |
| Arjun | 48000 |
| Rohan | 45000 |
Exercise: Selected cities
List customers who live in Mumbai, Delhi or Goa.
| customer_name | city |
|---|---|
| Ananya Sharma | Mumbai |
| Rohit Kumar | Delhi |
| David Fernandes | Goa |
Hint 1
Use IN with three text values.
SELECT customer_name, cityFROM customersWHERE city IN ('Mumbai', 'Delhi', 'Goa');| customer_name | city |
|---|---|
| Ananya Sharma | Mumbai |
| Rohit Kumar | Delhi |
| David Fernandes | Goa |
Quick quiz
1. price BETWEEN 100 AND 200 is the same as…
BETWEEN includes both boundary values.
2. What does WHERE id NOT IN (1, 2, NULL) return?
Comparing with NULL is UNKNOWN, so the condition is never true.
3. Which is the safest filter for a whole day on a DATETIME column?
A half-open range covers every time on that day.
IN and BETWEEN FAQ
Is SQL BETWEEN inclusive?
Yes. BETWEEN a AND b includes both a and b. It is equal to >= a AND <= b.
What is the difference between IN and BETWEEN?
IN matches a list of specific values. BETWEEN matches every value in a continuous range.
Why does NOT IN return no rows?
If the list or subquery contains NULL, NOT IN can never be true. Filter the NULLs out (WHERE col IS NOT NULL) or use NOT EXISTS.
How many values can I put in an IN list?
MySQL has no fixed limit, but very long lists are slow to parse. For thousands of values, load them into a table and join instead.