Транзакции в Oracle

Транзакции - это набор изменений данных, хранящихся в БД, который должен быть либо выполнен, либо отменен. До сих пор все примеры учебника, которые изменяли данные, как правило состояли из одного запроса, и все наши транзакции состояли из одного запроса. Если вы пользовались сервисом LiveSql, как мы рекомендовали, то беспокоиться о транзакциях не следовало. На самом деле, о них можно было не беспокоиться и в том случае, если примеры запускались для локально установленной БД Oracle, просто все изменения после закрытия соединения не были применены. Но в реальности изменения в данных часто представляют собой набор из нескольких запросов, и их результаты чаще всего нужно фиксировать.

Чтобы было понятнее, рассмотрим такой процесс, как копирование файлов. Предположим, какое-то количество файлов мы копируем на флешку или жесткий диск. Когда половина файлов уже была скопирована, возникла ошибка(любого рода - например, не хватило места на диске), или пользователь передумал и отменил операцию копирования. В этот момент копирование файлов прекращается, но те файлы, которые уже были скопированы, останутся на флешке/диске. Наша операция не была завершена, но состояние нашей системы не такое же, каким оно было до начала копирования.

Так вот, если бы копирование файлов было транзакцией, то после ее отмены, флешка/диск, на который мы копировали файлы, не содержала бы ни одного из копируемых файлов. А в том случае, если бы копирование прошло без прерываний, то все файлы, наоборот, гарантированно бы присутствовали в устройстве-получателе.

Рассмотрим такой процесс, как продажа автомобиля. Распишем, какие действия должны производиться при продаже:

  1. Количество доступных автомобилей должно уменьшиться на 1
  2. Должен быть составлен договор купли/продажи
  3. Мы должны сохранить информацию о том, кто продал автомобить, чтобы учесть это при начислении премии сотрудникам

Очевидно, что данный процесс должен быть произведен одной транзакцией, то есть все пункты этого процесса должны быть произведены, либо, в случае ошибки, или нашего собственного решения, ни одно из этих действий не возымело эффект. В противном случае, мы бы могли столкнуться с ситуацией, когда количество проданных машин не совпадает с количеством договоров купли-продажи (при условии, что на один автомобиль оформляется по одному договору), или наоборот, количество договоров о продаже авто больше, чем количество отсутствующих автомобилей, и так далее.

COMMIT. ROLLBACK

В Oracle транзакция начинается с того момента, как БД получает первый DML запрос и заканчивается либо закреплением транзакции, либо откатом всех изменений, произведенных в ней.

Транзакция продажи автомобиля может выглядеть подобным образом:

-- Уменьшаем количество автомобилей
update cars c
set c.count = c.count - 1
where c.id = 132;

-- Создаем договор
insert into orders(car_id, customer_name)
values(132, 'Иван Петров');

-- Сохраняем информацию о консультанте, продавшем авто
insert into consult_wrk(employee_name, car_id)
values('Петя Иванов', 132);

Вместе с первым запросом начинается транзакция. Все последующие запросы будут относиться к этой транзакции. Теперь главное - чтобы завершить транзакцию применением всех изменений, мы должны вызвать команду COMMIT, а чтобы отменить все изменения - вызывать команду ROLLBACK.

Почему до сих пор нигде в учебнике не использовались commit/rollback?

Как было сказано, Oracle начинает транзакцию при получении первого запроса, а точнее, первого запроса после предыдушей транзакции. Во многих IDE для коммита или отката транзакции предусмотрены отдельные элементы интерфейса - это могут быть кнопки, пункты меню и т.д. Конечно, завершить или откатить транзакцию можно и непосредственно вызовом commit или rollback.

Если примеры из учебника запускались в LiveSql, то там нет необходимости делать коммиты или роллбэки, т.к. мы работаем с временной базой, и если мы зайдем на этот сервис через час, нам заново придется создавать таблицы и добавлять туда данные.

В том случае, если Oracle ставился локально, нужно было делать коммит, чтобы изменения, произведенные при запуске dml запросов остались в базе.

Больше о транзакциях будет рассказано при рассмотрении языка программирования PL/SQL, где будет подробнее рассмотрено, когда и кто должен отправлять БД команды COMMIT и ROLLBACK.

Транзакции в многопользовательской среде

База данных редко работает только с одим клиентом. Чаще всего БД отвечает на запросы от большого числа клиентов, и многие их этих запросов приходят к ней в одно и то же время. Что будет, если во время выполнения нашей транзакции, описанной выше, другой клиент выполнит слудеющий запрос:

-- Получить количество договоров в системе
select count(*)
from orders

Ответ будет зависеть лишь от того, как была завершена транзакция. Если она была завершена вызовом COMMIT, то запрос вернет количество с учетом того договора, который был нами создан, а если транзакция завершилась вызовом ROLLBACK, либо вообще еще не завершилась(запросы еще выполняются, либо они выполнились, но ни ROLLBACK, ни COMMIT не вызывались), то новый договор не будет учтен в общем количестве, т.к. другие сессии еще знают о существовании нового договора.

Здесь нужно сделать уточнение. Даже если наша транзакция будет завершена вызовом COMMIT, запрос у другого клиента не будет брать в расчет изменения, произведенные нашей транзакцией, если наша транзакция была завершена позднее, чем запрос в другой сессии начал свое выполнение.

Итого

Все изменения в БД производятся в пределах транзакций. Транзакция может состоять как из одного запроса, так и из нескольких.

Commit фиксирует изменения в базе данных, rollback - отменяет.

-- Обновляем признак активности пользователя
-- ADMIN
update users u
set u.is_active = 'N'
where u.user_name = 'ADMIN';

-- Фиксируем транзакцию. Изменения сохраняются в БД
commit;

-- Теперь опять меняем флаг is_active на значение
-- 'N'. Т.к. изменения, произведенные предыдущим
-- запросом, были зафиксированы, этот запрос
-- начинает новую транзакцию.
update users u
set u.is_active = 'Y'
where u.user_name = 'ADMIN';

-- У пользователя ADMIN флаг is_active
-- по-прежнему равен 'N', т.к. действие
-- транзакции было отменено вызовом
-- rollback
rollback;

Транзакции могут состоять из набора изменений:

-- Уменьшаем количество автомобилей
update cars c
set c.count = c.count - 1
where c.id = 132;

-- Создаем договор
insert into orders(car_id, customer_name)
values(132, 'Иван Петров');

-- Сохраняем информацию о консультанте, продавшем авто
insert into consult_wrk(employee_name, car_id)
values('Петя Иванов', 132);

-- Изменения, произведенные тремя запросами, будут
-- сохранены в базе данных
commit;

Комментарии