Защита данных
Точки отката и субтранзакции
В идеале транзакции должны быть атомарными – такими же неделимыми, как представлялись атомы древним грекам. Однако в действительности даже атомы неделимыми не являются. С появлением реализации SQL: 1999 транзакции в базах данных также перестали быть атомарными. Транзакция теперь может состоять из множества субтранзакций. Субтранзакции отделяются друг от друга точками отката, задаваемыми с помощью оператора SAVEPOINT. Он может использоваться в сочетании с оператором ROLLBACK. До появления точек отката оператор ROLLBACK применялся только для отмены всей транзакции, теперь его можно использовать для отката транзакции до имеющейся в ней точки отката. Вы можете спросить: а для чего это нужно?
В основном оператор ROLLBACK применяется для восстановления исходных данных после того, как транзакция была прервана из-за ошибки. Понятно, что если во время транзакции произошла ошибка, то бессмысленно делать откат только к ближайшей точке отката. Ведь для того, чтобы вернуть базу данных в состояние, в котором она была перед началом транзакции, нужен откат всей транзакции. Но это еще не вся правда. Возникают ситуации, в которых нужен откат именно части транзакции.
Скажем, со своими данными вы проводите сложную последовательность операций. Где-то во время этого процесса получаются результаты, которые приводят вас к заключению, что вами выбран неверный путь. Если установить точку отката в соответствующем месте этой последовательности, можно выполнить откат к ней и попробовать другой вариант. Если нужно изменить действие лишь части кода, а остальной код является безупречным, то лучше поступить так, чем прекращать всю текущую транзакцию и запускать новую ради небольшого изменения.
Чтобы поместить в код SQL точку отката, используйте следующий синтаксис:
SAVEPOINT имя_точки_сохранения;
Откат транзакции к этой точке можно выполнить с помощью следующего кода:
ROLLBACK TO SAVEPOINT имя_точки_сохранения;
Некоторые реализации SQL не поддерживают оператор SAVEPOINT. Если в вашей реализации его нет, то использовать этот оператор вы не сможете.
Ограничения в транзакциях
Проверка достоверности данных заключается не только в проверке правильности типа данных. Кроме этого, возможно, потребуется следить, чтобы в некоторых столбцах не было неопределенных значений, а в других – значения не выходили за границы определенного диапазона. Об этих ограничениях см. в главе 5.
Говорить о связи ограничений с транзакциями имеет смысл потому, что первые влияют на работу последних. Предположим, например, что нужно добавить данные в таблицу, в которой имеется столбец с ограничением NOT NULL. Как правило, в начале создают пустую строку и затем заполняют ее значениями. Однако ограничение NOT NULL не позволит воспользоваться данным способом, поскольку SQL не даст ввести строку с неопределенным значением, находящимся в столбце с ограничением NOT NULL, даже если данные в этот столбец предполагается ввести еще до конца транзакции. Для решения этой проблемы в SQL:2003 существует возможность определять ограничения как DEFERRABLE (задерживаемые) или NOT DEFERRABLE (незадерживаемые).
Ограничения, определенные как NOT DEFERRABLE, применяются немедленно. А те, что определены как DEFERRABLE, первоначально могут быть заданы как DEFERRED (задержанные) или IMMEDIATE (немедленные). Если ограничение типа DEFERRABLE задано как IMMEDIATE, то оно действует так, как и ограничение типа NOT DEFERRABLE, – немедленно. Если же ограничение типа DEFERRABLE задано как DEFERRED, то его действие может быть отсрочено.
Чтобы добавлять пустые записи или выполнять другие операции, которые могут нарушить ограничения типа DEFERRABLE, можно использовать следующий оператор:
SET CONSTRAINTS ALL DEFERRED;
Он определяет все ограничения типа DEFERRABLE как DEFERRED. На ограничения типа NOT DEFERRABLE этот оператор не действует. После того как выполнены все операции, которые могут нарушить ваши ограничения, и таблица достигла того состояния, когда их нарушать уже нельзя, тогда эти ограничения можно применить заново. Соответствующий оператор выглядит следующим образом:
SET CONSTRAINTS ALL IMMEDIATE;
Если вы сделали ошибку и данные не соответствуют каким-либо ограничениям, вы сразу же это увидите после выполнения данного оператора.