sql >> Database >  >> RDS >> PostgreSQL

ROLLBACK-gebeurtenistriggers in postgresql

U kunt hiervoor geen reeks gebruiken. U hebt één enkel serialisatiepunt nodig waarmee alle inserts moeten gaan - anders kan het attribuut "gapless" niet worden gegarandeerd. U moet er ook voor zorgen dat er nooit rijen uit die tabel worden verwijderd.

De serialisatie betekent ook dat slechts een enkele transactie rijen in die tabel kan invoegen - alle andere invoegingen moeten wachten tot de "vorige" invoeging is vastgelegd of teruggedraaid.

Een patroon hoe dit kan worden geïmplementeerd, is om een ​​tabel te hebben waarin de "volgorde"-nummers worden opgeslagen. Laten we aannemen dat we dit nodig hebben voor factuurnummers die om juridische redenen spleetloos moeten zijn.

Dus we maken eerst de tabel om de "huidige waarde" vast te houden:

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

Nu hebben we een functie nodig die het volgende nummer genereert, maar die garandeert dat geen twee transacties tegelijkertijd het volgende nummer kunnen verkrijgen.

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;

De functie verhoogt de teller en retourneert de verhoogde waarde als resultaat. Vanwege de update de rij voor de reeks is nu vergrendeld en geen enkele andere transactie kan die waarde bijwerken. Als de aanroepende transactie wordt teruggedraaid, geldt dat ook voor de update van de sequentieteller. Als het is vastgelegd, blijft de nieuwe waarde behouden.

Om ervoor te zorgen dat elke transactie de functie gebruikt, moet er een trigger worden gemaakt.

Maak de betreffende tabel aan:

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

Maak nu de triggerfunctie en de trigger aan:

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

Als één transactie dit doet:

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

Het nieuwe nummer wordt gegenereerd. Een seconde transactie moet dan wachten tot de eerste insert is vastgelegd of teruggedraaid.

Zoals ik al zei:deze oplossing is niet schaalbaar. Helemaal niet. Het zal je applicatie enorm vertragen als er veel inserts in die tabel zitten. Maar je kunt niet beide hebben:een schaalbare en correcte implementatie van een gapless reeks.

Ik ben er ook vrij zeker van dat er randgevallen zijn die niet onder de bovenstaande code vallen. Het is dus vrij waarschijnlijk dat je nog steeds hiaten kunt krijgen.




  1. Oracle-procedure retourneert geen resultaten bij het uitvoeren van een scripttaak op SSIS

  2. SSH-tunneling gebruiken als VPN-alternatief

  3. PostgreSQL v13-implementatie en schalen met ClusterControl 1.8.2

  4. Tijdzones volledig negeren in Rails en PostgreSQL