SQL Tutorial › Querying Data › IN & BETWEEN

SQL IN and BETWEEN Operators

BeginnerLesson 13 of 699 min read10 queries run on MySQL 8.4

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.

Advertisement
In this lesson
  • Match a list of values with IN and NOT IN
  • Filter number and date ranges with BETWEEN
  • Remember that BETWEEN is inclusive
  • Avoid the NOT IN + NULL trap

Sample data

orders · 10 rows
order_idcustomer_idorder_datestatus
100112025-01-05Delivered
100222025-01-17Delivered
100312025-02-02Delivered
100432025-02-14Cancelled
100542025-03-08Delivered
100652025-03-22Shipped
100722025-04-10Delivered
100812025-04-28Pending
100932025-05-15Delivered
101052025-06-01Delivered

IN: match any value in a list

Orders from customers 1, 3 and 5:

MySQL
SELECT order_id, customer_id, statusFROM ordersWHERE customer_id IN (1, 3, 5);
ResultMySQL 8.4
order_idcustomer_idstatus
10011Delivered
10031Delivered
10081Pending
10043Cancelled
10093Delivered
10065Shipped
10105Delivered
7 rows in set
How this query works, step by step
  1. For each order, MySQL checks whether customer_id equals any value in the list.
  2. It is the same as customer_id = 1 OR customer_id = 3 OR customer_id = 5, but shorter.
  3. Customer 1 has three orders, customer 3 two and customer 5 two: 7 rows.

IN works with text too:

SQL
SELECT order_id, statusFROM ordersWHERE status IN ('Pending', 'Shipped');
ResultMySQL 8.4
order_idstatus
1006Shipped
1008Pending
2 rows in set

NOT IN

SQL
SELECT product_name, categoryFROM productsWHERE category NOT IN ('Electronics', 'Stationery');
ResultMySQL 8.4
product_namecategory
Office ChairFurniture
Standing DeskFurniture
Desk LampFurniture
Monitor ArmFurniture
4 rows in set
How this query works, step by step
  1. NOT IN keeps rows whose value matches none of the list.
  2. Only Furniture remains: 4 products.

IN with a subquery

The list can come from another query. Customers who have at least one order:

SQL
SELECT customer_id, customer_nameFROM customersWHERE customer_id IN (SELECT customer_id FROM orders);
ResultMySQL 8.4
customer_idcustomer_name
1Ananya Sharma
2Rohit Kumar
3Fatima Khan
4David Fernandes
5Ishita Bose
5 rows in set
How this query works, step by step
  1. The inner query returns the customer_id of every order (with repeats).
  2. 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:

SQL
SELECT first_nameFROM employeesWHERE emp_id NOT IN (SELECT manager_id FROM employees);
ResultMySQL 8.4
Empty set
How this query works, step by step
  1. The subquery returns every manager_id, including the CEO’s NULL.
  2. For an employee such as 103, MySQL checks 103 <> 101 AND 103 <> 100 AND ... AND 103 <> NULL.
  3. 103 <> NULL is UNKNOWN, so the whole AND chain is never TRUE: Empty set.

Exclude the NULLs and the query finds the employees who manage nobody:

SQL
SELECT first_nameFROM employeesWHERE emp_id NOT IN (SELECT manager_id FROM employees WHERE manager_id IS NOT NULL);
ResultMySQL 8.4
first_name
Priya
Karan
Neha
Arjun
Rohan
Sneha
Amit
7 rows in set
Watch out

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:

SQL
SELECT product_name, priceFROM productsWHERE price BETWEEN 1000 AND 4000ORDER BY price;
ResultMySQL 8.4
product_nameprice
Desk Lamp1499
USB-C Hub1999
Monitor Arm2799
Mechanical Keyboard3499
4 rows in set
How this query works, step by step
  1. BETWEEN 1000 AND 4000 means price >= 1000 AND price <= 4000.
  2. Both ends are included. A product priced exactly 1,000 or 4,000 would match.
  3. Four products fall in the range.

BETWEEN with dates

Orders placed in February and March 2025:

SQL
SELECT order_id, order_dateFROM ordersWHERE order_date BETWEEN '2025-02-01' AND '2025-03-31';
ResultMySQL 8.4
order_idorder_date
10032025-02-02
10042025-02-14
10052025-03-08
10062025-03-22
4 rows in set
Watch out

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

SQL
SELECT first_name, salaryFROM employeesWHERE salary NOT BETWEEN 50000 AND 100000ORDER BY salary;
ResultMySQL 8.4
first_namesalary
Amit25000
Rohan45000
Arjun48000
Vikram105000
Asha125000
Meera250000
6 rows in set
How this query works, step by step
  1. NOT BETWEEN 50000 AND 100000 keeps salaries below 50,000 or above 100,000.
  2. Three people earn less than 50,000 and three earn more than 100,000.

BETWEEN with text

BETWEEN also works on text, comparing alphabetically:

SQL
SELECT customer_nameFROM customersWHERE customer_name BETWEEN 'A' AND 'G'ORDER BY customer_name;
ResultMySQL 8.4
customer_name
Ananya Sharma
David Fernandes
Fatima Khan
3 rows in set

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.

Expected resultMySQL 8.4
first_namesalary
Priya72000
Karan72000
Neha54000
Arjun48000
Rohan45000
5 rows in set
Hint 1

BETWEEN 45000 AND 75000 includes both ends.

Exercise: Selected cities

List customers who live in Mumbai, Delhi or Goa.

Expected resultMySQL 8.4
customer_namecity
Ananya SharmaMumbai
Rohit KumarDelhi
David FernandesGoa
3 rows in set
Hint 1

Use IN with three text values.

Quick quiz

1. price BETWEEN 100 AND 200 is the same as…

2. What does WHERE id NOT IN (1, 2, NULL) return?

3. Which is the safest filter for a whole day on a DATETIME column?

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.