Using UPDATE with a Subquery in SQL

Ever found yourself needing to update a bunch of rows in your database, but the condition for the update depends on data from another table? That’s where UPDATE with a subquery comes in handy. Let’s break it down.

Sample Data

First, let’s set up some sample tables:

-- Create and populate the departments table
CREATE TABLE departments (
    dept_id INT PRIMARY KEY,
    dept_name VARCHAR(50),
    budget DECIMAL(10, 2)
);

INSERT INTO departments (dept_id, dept_name, budget)
VALUES 
    (1, 'HR', 500000),
    (2, 'IT', 1000000),
    (3, 'Sales', 750000);

-- Create and populate the employees table
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    name VARCHAR(50),
    dept_id INT,
    salary DECIMAL(10, 2)
);

INSERT INTO employees (emp_id, name, dept_id, salary)
VALUES 
    (1, 'Homer Simpson', 1, 60000),
    (2, 'Peter Griffin', 2, 75000),
    (3, 'Beatrice Johnson', 2, 65000),
    (4, 'Becky Brown', 3, 80000),
    (5, 'Charlie Davis', 3, 70000);

Now that we’ve got our tables set up, let’s start with a simple example. Say we want to give a 10% raise to all employees in the IT department.

Start with a SELECT Statement

When we use a subquery in the UPDATE statement, oftentimes we can use the same subquery that we’d use if we were simply selecting the rows we want to update.

Therefore, before going right ahead and writing the UPDATE statement, and then immediately running that against the database, it’s usually a good idea to start by selecting the data with a SELECT statement first. That way if we’ve made a mistake in our query logic, we can catch it and correct it before it’s too late.

So let’s go ahead with a SELECT statement:

SELECT * FROM employees WHERE dept_id = 2;

Result:

emp_id  name              dept_id  salary
------ ---------------- ------- ------
2 Peter Griffin 2 75000
3 Beatrice Johnson 2 65000

OK, so there are two employees in the IT department.

But wait! What if we don’t know the department ID for IT?

Well, that’s where the subquery comes in:

SELECT * FROM employees 
WHERE dept_id = (
    SELECT dept_id 
    FROM departments 
    WHERE dept_name = 'IT'
    );

Result:

emp_id  name              dept_id  salary
------ ---------------- ------- ------
2 Peter Griffin 2 75000
3 Beatrice Johnson 2 65000

Same result, as expected. All we needed to know was the department name, and the subquery went to work and dug up the corresponding ID for us. That was passed to the outer query and so the query was able to return the intended results.

So the same concept would apply if we wanted to update the table. We could use a subquery to identify the ID of the IT department.

So to reiterate, the good thing about running a SELECT statement first is that we can see which rows are going to be updated before we run the UPDATE statement. That way, if we’ve made a mistake in our query, we can correct it before updating any rows and making a complete mess of things.

Convert it to an UPDATE Statement

So we can now take our SELECT statement from above and modify the first couple of lines to convert it into an UPDATE statement:

UPDATE employees
SET salary = salary * 1.1
WHERE dept_id = (
    SELECT dept_id 
    FROM departments 
    WHERE dept_name = 'IT'
    );

So just to be sure, this subquery finds the ID of the IT department and then the outer UPDATE statement uses that ID in its WHERE clause.

Let’s check the result of the update:

SELECT * FROM employees 
WHERE dept_id = (
    SELECT dept_id 
    FROM departments 
    WHERE dept_name = 'IT'
    );

Result:

emp_id  name              dept_id  salary
------ ---------------- ------- ------
2 Peter Griffin 2 82500
3 Beatrice Johnson 2 71500

Nice to see that they’ve received their well deserved raise!

Another UPDATE with a Subquery

Let’s kick it up a notch. Suppose we want to give a 5% raise to all employees in departments with a budget over 700,000. Here’s how we can do that:

UPDATE employees
SET salary = salary * 1.05
WHERE dept_id IN (SELECT dept_id FROM departments WHERE budget > 700000);

In this case, our subquery might return multiple department IDs, so we use the IN operator instead of the equals operator (=).

Let’s take a look at the data now:

SELECT 
    e.name, 
    d.dept_name, 
    e.salary 
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

Result:

name              dept_name  salary
---------------- --------- ------
Homer Simpson HR 60000
Peter Griffin IT 86625
Beatrice Johnson IT 75075
Becky Brown Sales 84000
Charlie Davis Sales 73500

So the only person not to receive a raise was Homer Simpson from the HR department.

More Subqueries

Check out my article called Understanding the SQL Subquery that goes over various types of subqueries, along with examples.