In SQL Server, Common Table Expressions (CTEs) are often used for readability and simplifying complex queries. While CTEs are most commonly used when running a SELECT
query, we can also use CTEs to perform updates with the UPDATE
statement. This can be useful when we need to reference the same set of data multiple times or want to update records conditionally.
The Process
The process generally involves:
- Defining the CTE to identify or calculate the rows needing updates.
- Using the CTE results as the target for the
UPDATE
statement.
Example
Imagine we have a Customers
table and a BankAccounts
table. Each customer can have multiple bank accounts, which are listed as separate rows in the BankAccounts
table. And the Customers
table has a column that indicates whether the customer is “rich” or “poor” based on the total amounts in their bank accounts.
The following example uses this scenario, and we can use the CTE along with the UPDATE
statement to update the Customers
table for each customer based on their total balance in the BankAccounts
table.
Create Table and Populate with Initial Data
First, let’s create the tables for the customers and their bank balances. We’ll also populate the tables with initial data.
-- Create Customers table
CREATE TABLE Customers (
CustomerID INT IDENTITY(1,1) PRIMARY KEY,
FirstName NVARCHAR(100),
LastName NVARCHAR(100),
Status NVARCHAR(10) -- This will hold 'Poor' or 'Rich'
);
-- Create BankAccounts table
CREATE TABLE BankAccounts (
AccountID INT IDENTITY(1,1) PRIMARY KEY,
CustomerID INT,
Balance DECIMAL(10, 2),
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
-- Insert initial data into Customers
INSERT INTO Customers (FirstName, LastName, Status)
VALUES
('Aarav', 'Patel', NULL),
('Maria', 'Garcia', NULL),
('Li', 'Wei', NULL),
('Fatima', 'Al-Farsi', NULL),
('James', 'Smith', NULL);
-- Insert initial data into BankAccounts
INSERT INTO BankAccounts (CustomerID, Balance)
VALUES
(1, 500.00), -- Aarav Patel
(1, 1200.00), -- Aarav Patel
(2, 300.00), -- Maria Garcia
(3, 4000.00), -- Li Wei
(4, 50.00), -- Fatima Al-Farsi
(4, 30.00), -- Fatima Al-Farsi
(5, 2000.00), -- James Smith
(5, 3500.00); -- James Smith
-- Select data from the Customers table
SELECT * FROM Customers;
Output:
CustomerID FirstName LastName Status
---------- --------- -------- ------
1 Aarav Patel null
2 Maria Garcia null
3 Li Wei null
4 Fatima Al-Farsi null
5 James Smith null
We can see that the Status
column is currently full of NULL values. Our CTE will update with values, based on each customer’s bank balance.
Use a CTE to Update the Table
Let’s go ahead and create the CTE, then use an UPDATE
statement to update the table based on the result of the CTE:
-- Use CTE to calculate total balance for each customer
WITH CustomerBalances AS (
SELECT
C.CustomerID,
SUM(BA.Balance) AS TotalBalance
FROM
Customers C
INNER JOIN
BankAccounts BA ON C.CustomerID = BA.CustomerID
GROUP BY
C.CustomerID
)
-- Update Customers table based on total balance
UPDATE C
SET C.Status = CASE
WHEN CB.TotalBalance >= 3000 THEN 'Rich'
ELSE 'Poor'
END
FROM
Customers C
INNER JOIN
CustomerBalances CB ON C.CustomerID = CB.CustomerID;
-- Verify the updated Customers table
SELECT * FROM Customers;
Result:
CustomerID FirstName LastName Status
---------- --------- -------- ------
1 Aarav Patel Poor
2 Maria Garcia Poor
3 Li Wei Rich
4 Fatima Al-Farsi Poor
5 James Smith Rich
Now the Status
column has been updated based on the amount of money in their account.
Here’s an explanation of the code:
- CTE: The Common Table Expression (
CustomerBalances
) calculates the total balance for each customer by summing up the balances from theBankAccounts
table. - Update: The
UPDATE
statement uses the CTE to update theStatus
column in theCustomers
table. If a customer’s total balance is 3000 or more, they are marked asRich
; otherwise, they are marked asPoor
. - Validation: A
SELECT
statement is included at the end to display the updatedCustomers
table.