Продолжение транзакции после ошибки нарушения первичного ключа
Я делаю массовая вставка записей в базу данных из файла журнала. Иногда (~1 строка из каждой тысячи) одна из строк нарушает первичный ключ и вызывает сбой транзакции. В настоящее время пользователь должен вручную пройти через файл, вызвавший сбой, и удалить оскорбительную строку перед попыткой повторного импорта. Учитывая, что есть сотни этих файлов для импорта это непрактично.
мой вопрос: как я могу пропустить ввод записи, которые нарушат ограничение первичного ключа, без необходимости делать SELECT
оператор перед каждой строкой, чтобы увидеть, существует ли он уже?
примечание: Я знаю очень похожий вопрос #1054695, но это, похоже, конкретный ответ SQL Server, и я использую PostgreSQL (импорт через Python/psycopg2).
4 ответов
вы также можете использовать точки сохранения в транзакции.
Pythonish псевдокод иллюстрируется со стороны приложения:
database.execute("BEGIN")
foreach data_row in input_data_dictionary:
database.execute("SAVEPOINT bulk_savepoint")
try:
database.execute("INSERT", table, data_row)
except:
database.execute("ROLLBACK TO SAVEPOINT bulk_savepoint")
log_error(data_row)
error_count = error_count + 1
else:
database.execute("RELEASE SAVEPOINT bulk_savepoint")
if error_count > error_threshold:
database.execute("ROLLBACK")
else:
database.execute("COMMIT")
Edit: вот фактический пример этого в действии в psql на основе небольшого изменения примера в документации (операторы SQL с префиксом">"):
> CREATE TABLE table1 (test_field INTEGER NOT NULL PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "table1_pkey" for table "table1"
CREATE TABLE
> BEGIN;
BEGIN
> INSERT INTO table1 VALUES (1);
INSERT 0 1
> SAVEPOINT my_savepoint;
SAVEPOINT
> INSERT INTO table1 VALUES (1);
ERROR: duplicate key value violates unique constraint "table1_pkey"
> ROLLBACK TO SAVEPOINT my_savepoint;
ROLLBACK
> INSERT INTO table1 VALUES (3);
INSERT 0 1
> COMMIT;
COMMIT
> SELECT * FROM table1;
test_field
------------
1
3
(2 rows)
обратите внимание, что значение 3 было вставлено после ошибки, но все еще внутри той же транзакции!
документация для SAVEPOINT находится на http://www.postgresql.org/docs/8.4/static/sql-savepoint.html.
Я бы использовал хранимую процедуру, чтобы поймать исключения из ваших уникальных нарушений. Пример:
CREATE OR REPLACE FUNCTION my_insert(i_foo text, i_bar text)
RETURNS boolean LANGUAGE plpgsql AS
$BODY$
begin
insert into foo(x, y) values(i_foo, i_bar);
exception
when unique_violation THEN -- nothing
return true;
end;
$BODY$;
SELECT my_insert('value 1','another value');
можно сделать rollback
к транзакции или откату к точке сохранения непосредственно перед кодом, который вызывает исключение (CR-курсор):
name = uuid.uuid1().hex
cr.execute('SAVEPOINT "%s"' % name)
try:
# your failing query goes here
except Exception:
cr.execute('ROLLBACK TO SAVEPOINT "%s"' % name)
# your alternative code goes here
else:
cr.execute('RELEASE SAVEPOINT "%s"' % name)
этот код предполагает, что выполняется транзакция, иначе вы не получили бы это сообщение об ошибке.
в Django PostgreSQL в серверной создает курсоры непосредственно с psycopg. Возможно, в будущем они сделают прокси-класс для курсора Django, похожий на курсор оду. Они расширяют курсор с помощью следующий код (self является курсором):
@contextmanager
@check
def savepoint(self):
"""context manager entering in a new savepoint"""
name = uuid.uuid1().hex
self.execute('SAVEPOINT "%s"' % name)
try:
yield
except Exception:
self.execute('ROLLBACK TO SAVEPOINT "%s"' % name)
raise
else:
self.execute('RELEASE SAVEPOINT "%s"' % name)
таким образом, контекст упрощает ваш код, это будет:
try:
with cr.savepoint():
# your failing query goes here
except Exception:
# your alternative code goes here
и код более удобочитаем, потому что материала транзакции там нет.
или вы можете использовать SSIS, и неудачные строки принимают другой путь, чем успешные.
поскольку вы используете различную базу данных, можете ли вы массово вставлять файлы в промежуточную таблицу, а затем использовать код SQL для выбора только тех записей, у которых нет идентификатора exisitng?