#php #mysql #sql #transactions
Вопрос:
Я должен уменьшить деньги со счета пользователя и увеличить другой счет пользователя, а именно перевести деньги со счета на другой. У меня есть этот код, например, в MySQL:
START TRANSACTION;
UPDATE accounts
SET balance = (balance-100)
WHERE account_id = 2 AND balance>100;
--If the above query is succesfully then:
UPDATE accounts
SET balance = (balance 100)
WHERE account_id =1;
--How can I exec the commit only if everything is ok?
COMMIT;
Первый запрос выполняется только в том случае, если баланс>100.
Однако второй запрос (а именно второе обновление) следует выполнять только в том случае, если предыдущий запрос уменьшил баланс. Как я мог автоматически проверить это?
Кроме того, ФИКСАЦИЯ; должна выполняться только в том случае, если предыдущие 2 запроса выполнили свою работу.
Как это может быть реализовано?
(Я тоже использую PHP, но я думаю, что эту проблему можно легко решить с помощью sql. Я ошибаюсь?)
Комментарии:
1. Определите соответствующий ОБРАБОТЧИК ПРОДОЛЖЕНИЯ , установите некоторую переменную флага в своем коде, проанализируйте ее значение после выполнения инструкции, если предыдущая инструкция(инструкции) выдала какую-либо ошибку/предупреждение. Не забудьте удалить WHERE из 1-го запроса, но добавьте
CHECK (balance >= 0)
ограничение в определение таблицы, которое позволяет обнаружить проблему.2. Первый запрос выполняется только в том случае, если баланс>100.> Это неправильно !!! запрос выполняется в любом случае. Он изменяет данные только при выполнении условия — это верно.
3. @Akina, Конечно, я имел в виду, что изменение выполняется только в том случае, если баланс>100
Ответ №1:
Выполните операцию как отдельный запрос, а не как пакет запросов:
UPDATE accounts t1
CROSS JOIN accounts t2
SET t1.balance = (t1.balance-100),
t2.balance = (t2.balance 100)
WHERE t1.account_id = 2 AND t1.balance>100
AND t2.balance_id = 1;
-- or
UPDATE accounts
SET balance = balance CASE account_id WHEN 1 THEN 100
WHEN 2 THEN -100 END
WHERE account_id IN (1,2);
И вам вообще не нужно участвовать в транзакции.
Также вы можете проверить количество строк, измененных (фактически, на диске, а не формально) предыдущим запросом, и учесть эту информацию во 2-м запросе:
START TRANSACTION;
UPDATE accounts
SET balance = (balance-100)
WHERE account_id = 2 AND balance>100;
UPDATE accounts
SET balance = (balance 100)
WHERE account_id =1
AND ROW_COUNT(); -- check does a row was altered in previous statement
-- if not then this statement will not alter any row too
COMMIT;
Комментарии:
1. А что, если произойдет сбой при выполнении первого предложения set? Вся операция свернута?
2. @Umbert В 1 — м варианте (в обоих вариантах) — если запрос не будет выполнен по какой-либо причине, ни одна строка не будет обновлена. Запрос не может обновить только одну строку из двух сопоставленных строк — либо все, либо ни одной.
3. Таким образом, больше предложений набора в ОБНОВЛЕНИИ все выполнено или нет, то есть у них есть своего рода встроенный переход. Кроме того, перекрестное соединение (декартово произведение) в вашем первом решении необходимо для того, чтобы иметь возможность ссылаться на обе таблицы в НАБОРЕ?
4. @Umbert Один запрос-ЭТО ВСЕГДА транзакция. Исключение — макрофункции потока (например, ЗАГРУЗКА ДАННЫХ).