Пример реализации. Обработка выполнена в виде хранимой процедуры - чтобы не повторять сам запрос.
Процедура принимает номер ордера, который должен быть обработан. Если статус ордера равен 1, а баланс юзера не упадёт в минус, выполняется обработка, баланс корректируется, а статус ставится равным 2. В любом другом случае обработка не выполняется. Процедура возвращает 1, если обработка выполнена, и 0, если не выполнена.
Исходные данные
CREATE TABLE users (
id int,
balance int
);
CREATE TABLE orders (
id int,
amount int,
user_id int,
status int
);
INSERT INTO users VALUES (1,111), (2,222);
INSERT INTO orders VALUES (1,11,1,1), (2,22,1,1), (3,111,2,1), (4,222,2,1),(5,1,3,1),(6,1,1,0);
SELECT * FROM users;
SELECT * FROM orders;
id |
amount |
user_id |
status |
1 |
11 |
1 |
1 |
2 |
22 |
1 |
1 |
3 |
111 |
2 |
1 |
4 |
222 |
2 |
1 |
5 |
1 |
3 |
1 |
6 |
1 |
1 |
0 |
Процедура обработки
CREATE PROCEDURE process_order (IN order_id INT, OUT status BOOLEAN)
BEGIN
UPDATE users
JOIN orders ON users.id = orders.user_id
SET users.balance = users.balance - orders.amount,
orders.status = 2
WHERE orders.id = order_id
AND users.balance >= orders.amount -- проверка, что баланс достаточен
AND orders.status = 1; -- проверка, что статус единица
SET status = (ROW_COUNT() = 2);
END
CALL process_order(1, @status);
SELECT * FROM users;
SELECT * FROM orders;
SELECT @status;
Баланс достаточен, обработка выполняется.
id |
amount |
user_id |
status |
1 |
11 |
1 |
2 |
2 |
22 |
1 |
1 |
3 |
111 |
2 |
1 |
4 |
222 |
2 |
1 |
5 |
1 |
3 |
1 |
6 |
1 |
1 |
0 |
CALL process_order(2, @status);
SELECT * FROM users;
SELECT * FROM orders;
SELECT @status;
Баланс достаточен, обработка выполняется.
id |
amount |
user_id |
status |
1 |
11 |
1 |
2 |
2 |
22 |
1 |
2 |
3 |
111 |
2 |
1 |
4 |
222 |
2 |
1 |
5 |
1 |
3 |
1 |
6 |
1 |
1 |
0 |
CALL process_order(3, @status);
SELECT * FROM users;
SELECT * FROM orders;
SELECT @status;
Баланс достаточен, обработка выполняется.
id |
amount |
user_id |
status |
1 |
11 |
1 |
2 |
2 |
22 |
1 |
2 |
3 |
111 |
2 |
2 |
4 |
222 |
2 |
1 |
5 |
1 |
3 |
1 |
6 |
1 |
1 |
0 |
CALL process_order(4, @status);
SELECT * FROM users;
SELECT * FROM orders;
SELECT @status;
Баланс НЕдостаточен, обработка НЕ выполняется.
id |
amount |
user_id |
status |
1 |
11 |
1 |
2 |
2 |
22 |
1 |
2 |
3 |
111 |
2 |
2 |
4 |
222 |
2 |
1 |
5 |
1 |
3 |
1 |
6 |
1 |
1 |
0 |
CALL process_order(5, @status);
SELECT * FROM users;
SELECT * FROM orders;
SELECT @status;
Несуществующий юзер, обработка НЕ выполняется.
id |
amount |
user_id |
status |
1 |
11 |
1 |
2 |
2 |
22 |
1 |
2 |
3 |
111 |
2 |
2 |
4 |
222 |
2 |
1 |
5 |
1 |
3 |
1 |
6 |
1 |
1 |
0 |
CALL process_order(6, @status);
SELECT * FROM users;
SELECT * FROM orders;
SELECT @status;
Исходный статус НЕ единица, обработка НЕ выполняется.
id |
amount |
user_id |
status |
1 |
11 |
1 |
2 |
2 |
22 |
1 |
2 |
3 |
111 |
2 |
2 |
4 |
222 |
2 |
1 |
5 |
1 |
3 |
1 |
6 |
1 |
1 |
0 |
fiddle
PS. Рекомендую расширить диагностику, и в переменной статуса возвращать условную причину отказа в выполнении операции.
Также можно добавить в запрос любые дополнительные проверки.