PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

ROLLBACK-Ereignisauslöser in postgresql

Sie können dafür keine Sequenz verwenden. Sie benötigen einen einzelnen Serialisierungspunkt, durch den alle Beilagen müssen weg - sonst kann die Eigenschaft "lückenlos" nicht gewährleistet werden. Sie müssen auch sicherstellen, dass niemals Zeilen aus dieser Tabelle gelöscht werden.

Die Serialisierung bedeutet auch, dass nur eine einzige Transaktion Zeilen in diese Tabelle einfügen kann – alle anderen Einfügungen müssen warten, bis die „vorherige“ Einfügung festgeschrieben oder rückgängig gemacht wurde.

Ein Muster, wie dies implementiert werden kann, besteht darin, eine Tabelle zu haben, in der die "Folge"-Nummern gespeichert sind. Nehmen wir an, wir brauchen das für Rechnungsnummern, die aus gesetzlichen Gründen lückenlos sein müssen.

Also erstellen wir zuerst die Tabelle, um den "aktuellen Wert" zu halten:

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');

Jetzt brauchen wir eine Funktion, die die nächste Zahl generiert, aber garantiert, dass keine zwei Transaktionen gleichzeitig die nächste Zahl erhalten können.

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;

Die Funktion erhöht den Zähler und gibt als Ergebnis den erhöhten Wert zurück. Aufgrund des update Die Zeile für die Sequenz ist jetzt gesperrt und keine andere Transaktion kann diesen Wert aktualisieren. Wenn die aufrufende Transaktion zurückgesetzt wird, wird auch die Aktualisierung des Sequenzzählers zurückgesetzt. Wenn es festgeschrieben wird, wird der neue Wert beibehalten.

Um sicherzustellen, dass alle Transaktion die Funktion verwendet, sollte ein Trigger erstellt werden.

Erstellen Sie die betreffende Tabelle:

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

Erstellen Sie nun die Triggerfunktion und den Trigger:

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();

Wenn nun eine Transaktion dies tut:

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

Die neue Nummer wird generiert. Eine Sekunde Die Transaktion muss dann warten, bis die erste Einfügung festgeschrieben oder rückgängig gemacht wird.

Wie gesagt:Diese Lösung ist nicht skalierbar. Gar nicht. Es wird Ihre Anwendung massiv verlangsamen, wenn es viele Einfügungen in diese Tabelle gibt. Aber Sie können nicht beides haben:ein skalierbares und korrekte Implementierung einer lückenlosen Sequenz.

Ich bin mir auch ziemlich sicher, dass es Randfälle gibt, die nicht durch den obigen Code abgedeckt sind. Es ist also ziemlich wahrscheinlich, dass Sie immer noch Lücken haben.