2015-11-12 3 views
0

Я знаю, это может показаться странным, но есть ли способ, который я могу назвать своим триггером на событии ROLLBACK в таблице? Я просматривал документацию триггеров postgresql, есть события только для CREATE, UPDATE, DELETE и INSERT на таблице.Триггеры события ROLLBACK в postgresql

Мое требование заключается в транзакции ROLLBACK мой триггер выберет last_id из таблицы и сбросит последовательность таблиц со значением = last_id + 1; Короче говоря, я хочу сохранить значения последовательности при откате.

Любые идеи и обратная связь будут оценены ребятами!

+1

«Короче говоря, я хочу сохранить значения последовательности при откате *» - почему? Это звучит как полная трата времени и ресурсов. И это определенно не решение, если вам действительно нужны бесконечные последовательности. И _if_ вам *** действительно нужны бесконечные последовательности (например, по юридическим причинам), чем «последовательность» **, а не ** правильный инструмент для этого. –

+0

Мне действительно нужны бесцельные последовательности – Vish021

+0

@a_horse_with_no_name или есть способ получить текущее имя таблицы в транзакции? – Vish021

ответ

3

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

Сериализация также означает, что только одна транзакция может вставлять строки в эту таблицу - все остальные вставки должны ждать, пока «предыдущая» вставка не будет выполнена или откат.

Один образец, который может быть реализован, состоит в том, чтобы иметь таблицу, в которой хранятся номера «последовательности». Предположим, нам нужно это для номеров счетов, которые должны быть беспроблемными по юридическим причинам.

Таким образом, мы сначала создать таблицу для хранения «текущее значение»:

create table slow_sequence 
(
    seq_name  varchar(100) not null primary key, 
    current_value integer not null default 0 
); 

-- create a "sequence" for invoices 
insert into slow_sequence values ('invoice'); 

Теперь нам нужна функция, которая будет генерировать следующий номер, но это гарантирует, что никакие две транзакции не могут получить следующий номер на в то же время.

create or replace function next_number(p_seq_name text) 
    returns integer 
as 
$$ 
    update slow_sequence 
    set current_value = current_value + 1 
    where seq_name = p_seq_name 
    returning current_value; 
$$ 
language sql; 

Функция будет увеличивать счетчик и возвращать приращенное значение в результате. Из-за update строка для последовательности теперь заблокирована, и никакая другая транзакция не может обновить это значение. Если вызывающая транзакция откатывается назад, то это обновление для счетчика последовательности. Если он зафиксирован, новое значение сохраняется.

Чтобы гарантировать, что всякая операция использует эту функцию, должен быть создан триггер.

Создать таблицу в вопросе:

create table invoice 
(
    invoice_number integer not null primary key, 
    customer_id integer not null, 
    due_date  date not null 
); 

Теперь создать функцию триггера и триггер:

create or replace function f_invoice_trigger() 
    returns trigger 
as 
$$ 
begin 
    -- the number is assigned unconditionally so that this can't 
    -- be prevented by supplying a specific number 
    new.invoice_number := next_number('invoice'); 
    return new; 
end; 
$$ 
language plpgsql; 

create trigger invoice_trigger 
    before insert on invoice 
    for each row 
    execute procedure f_invoice_trigger(); 

Теперь если одна транзакция делает это:

insert into invoice (customer_id, due_date) 
values (42, date '2015-12-01'); 

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


Как я уже сказал, это решение не является масштабируемым. Не за что. Это значительно замедлит ваше приложение, если в эту таблицу будет много вставок. Но вы не можете иметь оба: масштабируемые и правильной реализации бесщелевой последовательности.

Я также уверен, что есть краевой кейс, который не подпадает под вышеуказанный код. Поэтому довольно вероятно, что вы все еще можете столкнуться с пробелами.

+0

Да, я согласен, у меня может быть несколько вставок, и это не масштабируется. Есть ли способ получить текущее имя таблицы в транзакции? 'pg_locks' имеет столбец с именем отношение, но он дает OID. Как получить текущее имя таблицы в транзакции? – Vish021

+0

Вы можете указать имя из OID или просто посмотреть его в pg_class. Подсказка: вы также хотите присоединиться к pg_namespace для имени схемы. –

+5

@ Vish021: * правильное * решение бесщелевого номера ** никогда не масштабируется. Это не может быть –

Смежные вопросы