PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Zdarzenie ROLLBACK wyzwalacze w postgresql

Nie możesz użyć do tego sekwencji. Potrzebujesz jednego punktu serializacji, przez który wszystkie wstawki muszą zostać usunięte - w przeciwnym razie nie można zagwarantować atrybutu „bez przerw”. Musisz także upewnić się, że żadne wiersze nigdy nie zostaną usunięte z tej tabeli.

Serializacja oznacza również, że tylko jedna transakcja może wstawić wiersze do tej tabeli — wszystkie inne wstawienia muszą poczekać, aż „poprzednie” wstawienie zostanie zatwierdzone lub wycofane.

Jednym ze wzorców, w jaki sposób można to zaimplementować, jest posiadanie tabeli, w której przechowywane są numery „sekwencji”. Załóżmy, że potrzebujemy tego dla numerów faktur, które muszą być bez przerw ze względów prawnych.

Dlatego najpierw tworzymy tabelę do przechowywania „bieżącej wartości”:

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

Teraz potrzebujemy funkcji, która wygeneruje następną liczbę, ale która gwarantuje, że żadne dwie transakcje nie mogą uzyskać kolejnego numeru w tym samym czasie.

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;

Funkcja zwiększy licznik i zwróci w rezultacie zwiększoną wartość. Z powodu update wiersz sekwencji jest teraz zablokowany i żadna inna transakcja nie może zaktualizować tej wartości. Jeśli transakcja wywołująca zostanie wycofana, tak samo jest z aktualizacją licznika sekwencji. Jeśli zostanie zatwierdzony, nowa wartość zostanie zachowana.

Aby upewnić się, że każdy transakcja korzysta z tej funkcji, należy utworzyć wyzwalacz.

Utwórz odpowiednią tabelę:

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

Teraz utwórz funkcję wyzwalacza i wyzwalacz:

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

Teraz, jeśli jedna transakcja to zrobi:

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

Generowany jest nowy numer. sekunda transakcja musi wtedy poczekać, aż pierwsza wstawka zostanie zatwierdzona lub wycofana.

Jak powiedziałem:to rozwiązanie nie jest skalowalne. Zupełnie nie. Spowoduje to znaczne spowolnienie aplikacji, jeśli w tej tabeli będzie dużo wstawek. Ale nie możesz mieć obu:skalowalnego i poprawna implementacja sekwencji bez przerw.

Jestem również prawie pewien, że istnieją przypadki brzegowe, których nie obejmuje powyższy kod. Więc jest całkiem prawdopodobne, że nadal możesz skończyć z lukami.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Pierwsze kroki z replikacją strumieniową PostgreSQL

  2. sqlalchemy.exc.NoSuchModuleError:Nie można załadować wtyczki:sqlalchemy.dialects:postgres

  3. Jak wykonać tę samą agregację w każdej kolumnie, nie wymieniając kolumn?

  4. Jak mogę scalić kolumny z dwóch tabel w jedno wyjście?

  5. Jak ocenić wyrażenie w instrukcji select w Postgres