Ключевые слова:postgresql, sql, transaction, (найти похожие документы)
From: Феськов Кузьма <kuzma@russofile.ru.>
Date: Wed, 10 Dec 2006 18:21:07 +0000 (UTC)
Subject: Транзакции и точки сохранения в PostgreSQL 8.0
Оригинал: http://php.russofile.ru/pg_8_transactions.html
Джошуа Д. Дрейк: Оригинал на http://www.devx.com/dbzone/Article/22119
Перевод Феськов Кузьма Б[email protected]>, http://php.russofile.ru
PostgreSQL версии 8.0 Транзакции - точки сохранения - помогут сохранить ваши данные
Несомненно, транзакции очень хороши, но текущие транзакции PostgreSQL
пропагандировали лозунг - "все, или ничего", останавливая транзакцию,
если ошибка произошла в ее пределах. К счастью, новая версия
PostgreSQL 8 позволяет взглянуть на это подругому, добавляя
"savepoints" (точки сохранения), позволяя откатить назад только часть
транзакции и восстановиться от ошибки изящно.
Джошуа Д. Дрейк
Одна из очень хороших особенностей PostgreSQL - транзакции. Они
предотвращают случайную потерю данных или их искажение.
Например, скажем вы хотите удалить записи в таблице. В PostgreSQL
команда выглядит так:
template1=# DELETE FROM foo;
Однако, данная команда удалить все записи в таблице. Это, вероятно,
вовсе не то, чего вы хотите, и, если вы не использовали транзакцию,
восстановление из резервной копии базы будет единственным способом.
Используя транзакции, вернуть данные очень просто:
BEGIN;
DELETE FROM foo;
DELETE 50
Параметр BEGIN заставляет базу начать транзакцию. Поскольку, скоро вы
понимаете, что забыли включить параметр WHERE и удалили все столбцы,
то вам необходимо будет сделать обратную перемотку и восстановить
данные:
BEGIN;
DELETE FROM foo;
DELETE 50
ROLLBACK;
Есть один недостаток в текущей реализации транзакций - если внутри
транзакции произошла ошибка, вы вынуждены сделать перемотку. Команда
перемотки должна будет выполнена в пределах транзакции, до того, как
произведены какие-либо команды в пределах соединения. Как только
перемотка будет выполнена, вы должны будете повторить действия снова в
том виде, который не будет вызывать ошибку. Это правило включает в
себя и ошибки пользователей, типа удаления всех отчетов в таблице,
синтаксические ошибки, типа попытки выбрать из столбца, который не
существует. Например:
BEGIN;
UPDATE foo SET bar = (SELECT count(*) FROM baz));
INSERT INTO foo (column1) SELECT column2 FROM bar;
OШИБКА: отношение "bar" не существует
CREATE TABLE bar (column1 text, column2 float);
OШИБКА: текущая транзакция прервана команды, игнорируемые до конца блока
Из-за ошибки вы будете делать перемотку и вся ваша текущая работа
будет потеряна. Этот специфический момент транзакций PostgreSQL
особенно раздражает в период отладки и тестирования.
Точки сохранения - путь к спасению.
Новая версия PostgreSQL 8 обращается к этой проблеме с помощью точек
сохранения 'savepoints'. Точки сохранения, названные 'placeholders'
(держатели места), которые заставляют базу данных сохранять состояние
в текущей точке в пределах транзакции - это называется 'savepoint'
(точка сохранения). Если ошибка происходит, но после savepoint, вы
можете сделать перемотку обратно до точки сохранения, не теряя при
этом работу, которая лежит перед savepoint. Каждая точка сохранения
имеет имя.
Чтобы инициализировать savepoint, вы должны быть в пределах
операционного блока:
template1=# BEGIN;
BEGIN
template1=# INSERT INTO foo(column1,column2,column3) VALUES (1,2,0);
INSERT 17231 1
template1=# INSERT INTO foo(column1,column2,column3) VALUES (1,2,0);
INSERT 17232 1
template1=# INSERT INTO foo(column1,column2,column3) VALUES (1,2,0);
INSERT 17233 1
template1=# SELECT * FROM foo;
column1 | column2 | column3
---------+--------+--------
1 | 2 | 0
1 | 2 | 0
1 | 2 | 0
(3 rows)
template1=# SAVEPOINT main_values_inserted;
SAVEPOINT
template1=# INSERT INTO foo(column1,column2,column3) VALUES (1,2,1/0);
OШИБКА: деление на ноль
OШИБКА: деление на ноль
В текущей PostgreSQL версии, вы потеряли бы все INSERT данные в
предыдущем коде, когда возникла ошибка деления на ноль в последнем
утверждении INSERT, но в версии 8 вы можете сделать перемотку до
определенного savepoint. Обратите внимание, что код содержит savepoint
с именем 'main_values_inserted'. Для обратной перемотки до этой
savepoint вы можете написать:
template1=# ROLLBACK TO main_values_inserted;
ROLLBACK
Выполняя перемотку до этой savepoint вы сохраняете все, что было до
нее, теряя только работу выполненную после savepoint. После перемотки
вы можете продолжить свою работу без полного повтора транзакции:
template1=# INSERT INTO foo(column1,column2,column3) VALUES (5,9,10);
INSERT 17234 1
template1=# INSERT INTO foo(column1,column2,column3) VALUES (5,9,10);
INSERT 17235 1
template1=# INSERT INTO foo(column1,column2,column3) VALUES (5,9,10);
INSERT 17236 1
template1=# INSERT INTO foo(column1,column2,column3) VALUES (5,9,10);
INSERT 17237 1
template1=# SAVEPOINT secondary_values_inserted;
SAVEPOINT
template1=# SELECT * FROM foo;
column1 | column2 | column3
---------+--------+--------
1 | 2 | 0
1 | 2 | 0
1 | 2 | 0
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
(7 rows)
template1=# SAVEPOINT all_values_inserted;
SAVEPOINT
template1=# DELETE FROM foo;
DELETE 7
template1=# SELECT * FROM foo;
column1 | column2 | column3
---------+--------+--------
(0 rows)
Ой. Также, как и в первом примере этой статьи, вы фактически хотели
удалить только одну строку где 'column = 1'. Но вы можете сделать
перемотку ко второй savepoint 'all_values_inserted' в коде выше.
template1=# ROLLBACK TO all_values_inserted;
ROLLBACK
template1=# SELECT * FROM foo;
column1 | column2 | column3
---------+--------+--------
1 | 2 | 0
1 | 2 | 0
1 | 2 | 0
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
(7 rows)
Обратите внимание, это восстановило все ваши данные. Теперь вы можете
запустить корректные команды для удаления.
template1=# DELETE FROM foo WHERE column1 = 1;
DELETE 3
template1=# SELECT * FROM foo;
column1 | column2 | column3
---------+--------+--------
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
(4 rows)
Наконец, вы можете завершить вашу транзакцию. Последняя команда SELECT
показывает, что целостность данных после всех этих вставок и обратных
перемоток не повреждена.
template1=# COMMIT;
COMMIT
template1=# select * from foo;
column1 | column2 | column3
---------+--------+--------
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
5 | 9 | 10
(4 rows)
Джошуа Д. Дрейк - Президент Command Prompt, Inc. Компания занимается
поддержкой PostgreSQL программированием. Он также соавтор
'Практического PostgreSQL' от издательства O'reilly и Партнеров.