Transactions
Выдержки из Тома Кайта.
Транзакции в Oracle удовлетворяют всем требуемым характеристикам ACID.
Аббревиатура ACID означает:
• атомарность (atomicity) — выполняется либо вся транзакция целиком, либо она целиком не выполняется;
• согласованность (consistency) — транзакция переводит базу данных из одного согласованного состояния в другое;
• изоляция (isolation) — эффект от транзакции не виден другим транзакциям до тех пор, пока она не будет зафиксирована;
• устойчивость (durability) — как только транзакция зафиксирована, она остается постоянной.
Атомарность
Рассмотрим пример:
Создадим таблицы T(вставляем сюда строки) и T2(содержит счётчик строк в таблице T), и триггер, который при вставке строки в T, увеличивает значение счётчика в Т2, при удалении - уменьшает.
Oracle делает исходный оператор INSERT атомарным — то есть первоначальный INSERT INTO T является оператором, а любой сторонний эффект этого оператора рассматривается как его часть.
СУБД Oracle достигает такой атомарности уровня оператора за счет того, что молча снабжает конструкцией SAVEPOINT каждый вызов в базе данных. Преды дущие два INSERT на самом деле трактуются так:
Savepoint statement1;
Insert into t values ( 1 );
If error then rollback to statement1;
Savepoint statement2;
Insert into t values ( -1 );
If error then rollback to statement2;
В Oracle атомарность уровня оператора распространяется так глубоко, как это должно быть. Если в предыдущем примере INSERT INTO T возбудит триггер, обновляющий другую таблицу, а та таблица снабжена триггером, выполняющим удаление в третьей таблице (и так далее, и тому подобное), то либо вся работа будет выполнена успешно, либо никакая выполнена не будет. Вам не нужно ничего дополнительно кодировать, чтобы обеспечить это — просто так оно работает.
Атомарность уровня процедуры
Используем те же таблицы Т и Т2, вернёмся к начальным условиям - удалим все строки из Т и установим cnt=0 в Т2.
Создадим процедуру, с 2мя инсертами в ней, 1й правильный, а второй нарушает ограничение целостности.
Запустим процедуру:
Проверяем, что в таблицах Т и Т2:
Oracle трактует вызов хранимой процедуры как атомарный оператор. Клиент посылает блок кода BEGIN P; END;, а Oracle обертывает его в SAVEPOINT. Поскольку процедура P терпит неудачу, Oracle восстанавливает базу данных в состояние, которое она имела в точке, непосредственно предшествовавшей ее вызову.
Изменим блок вызова процедуры:
Проверяем, что в таблицах Т и Т2:
Мы добавили к вызову процедуры блок кода, чтобы все возможные ошибки игнорировались. Процедура завершается успехом за счёт самостоятельного перехвата и игнорирования ошибок.
Поэтому частичная работа процедуры p сохраняется - 1й инсерт проходит, 2й неправильный нет.
Том Кайт считает ошибочным практически любой код, который содержит обработчик исключений WHEN OTHERS, но не включает вызова RAISE, чтобы передать исключение выше.
Выдержки из Тома Кайта.
Транзакции в Oracle удовлетворяют всем требуемым характеристикам ACID.
Аббревиатура ACID означает:
• атомарность (atomicity) — выполняется либо вся транзакция целиком, либо она целиком не выполняется;
• согласованность (consistency) — транзакция переводит базу данных из одного согласованного состояния в другое;
• изоляция (isolation) — эффект от транзакции не виден другим транзакциям до тех пор, пока она не будет зафиксирована;
• устойчивость (durability) — как только транзакция зафиксирована, она остается постоянной.
Атомарность
Рассмотрим пример:
Создадим таблицы T(вставляем сюда строки) и T2(содержит счётчик строк в таблице T), и триггер, который при вставке строки в T, увеличивает значение счётчика в Т2, при удалении - уменьшает.
-- Т
CREATE TABLE t ( x INT CHECK ( x>0 ) );
-- Т2
CREATE TABLE t2 (cnt INT);
-- триггер
CREATE TRIGGER t_trigger
BEFORE INSERT OR DELETE
ON guppi.t
FOR EACH ROW
BEGIN
IF (INSERTING)
THEN
UPDATE t2
SET cnt = cnt + 1;
ELSE
UPDATE t2
SET cnt = cnt - 1;
END IF;
DBMS_OUTPUT.put_line ('Я вставил и обновил ' || SQL%ROWCOUNT || ' строк(у)');
END;
-- вставим в Т2 назчальное значение счётчика cnt=0, если таблица Т2 будет пустая, то триггер не -- сможет работать, ему просто нечего будет обновлять
insert into t2 values(0);
-- вставим в Т строку
insert into t values(1);
Я вставил и обновил 1 строку
В Т2 cnt = 1.
-- вставим в Т строку нарушающую ограничение (x INT CHECK ( x>0 ))
insert into t values(-1);
Я вставил и обновил 1 строк(у)
insert into t values(-1)
ORA-02290: check constraint (GUPPI.SYS_C0079627) violated
Сообщение "Я вставил и обновил 1 строк(у)" показывает, что триггер сработал, но мы нарушили ограничение целостности ORA-02290 строка в Т вставлена не была:
SQL> select * from t;
X
---------------------------
1
Какое же тогда значение счётчика в Т2, ведь триггер сработал?
SQL> select * from t2;
CNT
-----------------------------
1
Ожидалось увидеть cnt=2, но нет, cnt=1.
insert into t2 values(0);
-- вставим в Т строку
insert into t values(1);
Я вставил и обновил 1 строку
В Т2 cnt = 1.
-- вставим в Т строку нарушающую ограничение (x INT CHECK ( x>0 ))
insert into t values(-1);
Я вставил и обновил 1 строк(у)
insert into t values(-1)
ORA-02290: check constraint (GUPPI.SYS_C0079627) violated
Сообщение "Я вставил и обновил 1 строк(у)" показывает, что триггер сработал, но мы нарушили ограничение целостности ORA-02290 строка в Т вставлена не была:
SQL> select * from t;
X
---------------------------
1
Какое же тогда значение счётчика в Т2, ведь триггер сработал?
SQL> select * from t2;
CNT
-----------------------------
1
Ожидалось увидеть cnt=2, но нет, cnt=1.
Oracle делает исходный оператор INSERT атомарным — то есть первоначальный INSERT INTO T является оператором, а любой сторонний эффект этого оператора рассматривается как его часть.
СУБД Oracle достигает такой атомарности уровня оператора за счет того, что молча снабжает конструкцией SAVEPOINT каждый вызов в базе данных. Преды дущие два INSERT на самом деле трактуются так:
Savepoint statement1;
Insert into t values ( 1 );
If error then rollback to statement1;
Savepoint statement2;
Insert into t values ( -1 );
If error then rollback to statement2;
В Oracle атомарность уровня оператора распространяется так глубоко, как это должно быть. Если в предыдущем примере INSERT INTO T возбудит триггер, обновляющий другую таблицу, а та таблица снабжена триггером, выполняющим удаление в третьей таблице (и так далее, и тому подобное), то либо вся работа будет выполнена успешно, либо никакая выполнена не будет. Вам не нужно ничего дополнительно кодировать, чтобы обеспечить это — просто так оно работает.
Атомарность уровня процедуры
Используем те же таблицы Т и Т2, вернёмся к начальным условиям - удалим все строки из Т и установим cnt=0 в Т2.
Создадим процедуру, с 2мя инсертами в ней, 1й правильный, а второй нарушает ограничение целостности.
create or replace procedure p as
begin
insert into t values (1);
insert into t values (-1);
end;
begin
insert into t values (1);
insert into t values (-1);
end;
Запустим процедуру:
SQL> exec p;
Я вставил и обновил 1 строк(у)
Я вставил и обновил 1 строк(у)
begin p; end;
ORA-02290: check constraint (GUPPI.SYS_C0079627) violated
ORA-06512: at "GUPPI.P", line 4
ORA-06512: at line 2
Я вставил и обновил 1 строк(у)
Я вставил и обновил 1 строк(у)
begin p; end;
ORA-02290: check constraint (GUPPI.SYS_C0079627) violated
ORA-06512: at "GUPPI.P", line 4
ORA-06512: at line 2
Проверяем, что в таблицах Т и Т2:
SQL> select * from t;
X
---------------------------------------
SQL> select * from t2;
CNT
---------------------------------------
0
X
---------------------------------------
SQL> select * from t2;
CNT
---------------------------------------
0
Oracle трактует вызов хранимой процедуры как атомарный оператор. Клиент посылает блок кода BEGIN P; END;, а Oracle обертывает его в SAVEPOINT. Поскольку процедура P терпит неудачу, Oracle восстанавливает базу данных в состояние, которое она имела в точке, непосредственно предшествовавшей ее вызову.
Изменим блок вызова процедуры:
SQL> begin p;
2 exception
3 when others then null;
4 end;
5 /
Я вставил и обновил 1 строк(у)
Я вставил и обновил 1 строк(у)
PL/SQL procedure successfully completed
2 exception
3 when others then null;
4 end;
5 /
Я вставил и обновил 1 строк(у)
Я вставил и обновил 1 строк(у)
PL/SQL procedure successfully completed
Проверяем, что в таблицах Т и Т2:
SQL> select * from t;
X
---------------------------------------
1
SQL> select * from t2;
CNT
---------------------------------------
1
X
---------------------------------------
1
SQL> select * from t2;
CNT
---------------------------------------
1
Мы добавили к вызову процедуры блок кода, чтобы все возможные ошибки игнорировались. Процедура завершается успехом за счёт самостоятельного перехвата и игнорирования ошибок.
Поэтому частичная работа процедуры p сохраняется - 1й инсерт проходит, 2й неправильный нет.
Том Кайт считает ошибочным практически любой код, который содержит обработчик исключений WHEN OTHERS, но не включает вызова RAISE, чтобы передать исключение выше.