I encountered an unexpected behavior in MariaDB when performing an UPDATE statement with multiple SET clauses. Here's the setup:
Table Creation and Initial Data
CREATE OR REPLACE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
category VARCHAR(50),
price DECIMAL(10,2),
discount_price DECIMAL(10,2)
);
INSERT INTO products (name, category, price, discount_price) VALUES
('Laptop', 'electronics', 1000.00, 900.00),
('Smartphone', 'electronics', 800.00, 720.00),
('Tablet', 'electronics', 500.00, 450.00),
('Headphones', 'accessories', 200.00, 180.00);
Case 1: Expected Behavior
When I run the following query:
UPDATE products
SET price = price * 1.10,
discount_price = price * 0.90
WHERE category = 'electronics';
The SET clauses evaluate sequentially, as expected: price is updated first (price = price * 1.10). Then discount_price is calculated based on the updated value of price. This produces the correct results.
Case 2: Unexpected Behavior
However, when I use a subquery in the WHERE clause:
UPDATE products
SET price = price * 1.10,
discount_price = price * 0.90
WHERE id IN (SELECT id FROM products WHERE category = 'electronics');
The behavior changes: it seems like the SET clauses are evaluated simultaneously, not sequentially. As a result:
The discount_price is calculated based on the old value of price, instead of the updated value. This leads to incorrect results for discount_price.
My Understanding
From my knowledge of MariaDB's behavior, multiple SET clauses in an UPDATE statement should be evaluated sequentially from left to right. This works as expected when using a simple condition in the WHERE clause but behaves differently when a subquery is involved.
Question
Is this behavior a bug in MariaDB? Or is there some underlying reason why the presence of a subquery in the WHERE clause causes the SET clauses to evaluate simultaneously? How can I ensure sequential evaluation in such cases?
Environment
MariaDB version: 10.6.20 (AWS RDS Mariadb)
I encountered an unexpected behavior in MariaDB when performing an UPDATE statement with multiple SET clauses. Here's the setup:
Table Creation and Initial Data
CREATE OR REPLACE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
category VARCHAR(50),
price DECIMAL(10,2),
discount_price DECIMAL(10,2)
);
INSERT INTO products (name, category, price, discount_price) VALUES
('Laptop', 'electronics', 1000.00, 900.00),
('Smartphone', 'electronics', 800.00, 720.00),
('Tablet', 'electronics', 500.00, 450.00),
('Headphones', 'accessories', 200.00, 180.00);
Case 1: Expected Behavior
When I run the following query:
UPDATE products
SET price = price * 1.10,
discount_price = price * 0.90
WHERE category = 'electronics';
The SET clauses evaluate sequentially, as expected: price is updated first (price = price * 1.10). Then discount_price is calculated based on the updated value of price. This produces the correct results.
Case 2: Unexpected Behavior
However, when I use a subquery in the WHERE clause:
UPDATE products
SET price = price * 1.10,
discount_price = price * 0.90
WHERE id IN (SELECT id FROM products WHERE category = 'electronics');
The behavior changes: it seems like the SET clauses are evaluated simultaneously, not sequentially. As a result:
The discount_price is calculated based on the old value of price, instead of the updated value. This leads to incorrect results for discount_price.
My Understanding
From my knowledge of MariaDB's behavior, multiple SET clauses in an UPDATE statement should be evaluated sequentially from left to right. This works as expected when using a simple condition in the WHERE clause but behaves differently when a subquery is involved.
Question
Is this behavior a bug in MariaDB? Or is there some underlying reason why the presence of a subquery in the WHERE clause causes the SET clauses to evaluate simultaneously? How can I ensure sequential evaluation in such cases?
Environment
MariaDB version: 10.6.20 (AWS RDS Mariadb)
Share Improve this question asked Mar 9 at 14:14 Ozzy BlackOzzy Black 836 bronze badges 12 | Show 7 more comments1 Answer
Reset to default 0I personally like to solve this (also use it in fairly large queries just for readability) with user defined variables.
So partial results can be reused.
You can write the query like this:
SET @oldPrice=0;
SET @newPrice=0;
UPDATE
`products`
SET
`price` = IF(((@oldPrice:=`price`) OR TRUE) AND ((@newPrice:=(@oldPrice * 1.1)) OR TRUE), (SELECT @newPrice), (SELECT @newPrice)),
`discount_price` = (@newPrice * 0.9)
WHERE
`id` IN (SELECT id FROM `products` WHERE `category` = 'electronics');
Which results in:
id | name | category | price | discount_price |
---|---|---|---|---|
1 | Laptop | electronics | 1100.00 | 990.00 |
2 | Smartphone | electronics | 880.00 | 792.00 |
3 | Tablet | electronics | 550.00 | 495.00 |
4 | Headphones | accessories | 200.00 | 180.00 |
The @oldPrice
variable is not really needed here, but ok.
A fiddle https://dbfiddle.uk/30BeFMNE
UPDATE products JOIN (SELECT … newprice, … newdiscountprice FROM products) newprice ON newprice.id = products.id SET products.price = newprice.price etc.
as in this fiddle? As the SELECT explicitely computes from old values. – Guillaume Outters Commented Mar 9 at 17:14