sql >> Database teknologi >  >> RDS >> PostgreSQL

ROLLBACK hændelsestriggere i postgresql

Du kan ikke bruge en sekvens til dette. Du har brug for et enkelt serialiseringspunkt, hvorigennem alle indsatser skal gå - ellers kan attributten "gapless" ikke garanteres. Du skal også sikre dig, at ingen rækker nogensinde bliver slettet fra den tabel.

Serialiseringen betyder også, at kun en enkelt transaktion kan indsætte rækker i den tabel - alle andre inserts skal vente, indtil den "forrige" insert er blevet begået eller rullet tilbage.

Et mønster, hvordan dette kan implementeres, er at have en tabel, hvor "sekvens"-numrene er gemt. Lad os antage, at vi har brug for dette til fakturanumre, der skal være hulfri af juridiske årsager.

Så vi opretter først tabellen til at holde den "aktuelle værdi":

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 har vi brug for en funktion, der vil generere det næste tal, men som garanterer, at ikke to transaktioner kan opnå det næste tal på samme tid.

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;

Funktionen vil øge tælleren og returnere den øgede værdi som et resultat. På grund af update rækken for sekvensen er nu låst, og ingen anden transaktion kan opdatere denne værdi. Hvis den opkaldende transaktion rulles tilbage, er opdateringen til sekvenstælleren også det. Hvis den er forpligtet, bevares den nye værdi.

For at sikre, at hver transaktion bruger funktionen, skal der oprettes en trigger.

Opret den pågældende tabel:

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

Opret nu triggerfunktionen og triggeren:

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

Hvis en transaktion nu gør dette:

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

Det nye nummer genereres. Et sekund transaktionen skal derefter vente, indtil den første indsættelse er begået eller rullet tilbage.

Som sagt:denne løsning er ikke skalerbar. Slet ikke. Det vil sænke din applikation enormt, hvis der er mange indlæg i den tabel. Men du kan ikke have begge dele:et skalerbart og korrekt implementering af en mellemrumssekvens.

Jeg er også ret sikker på, at der er edge-sager, der ikke er omfattet af ovenstående kode. Så det er ret sandsynligt, at du stadig kan ende med huller.




  1. Hvordan indsætter man pandas dataramme via mysqldb i databasen?

  2. How to_char() virker i PostgreSQL

  3. Hvordan finder man summen af ​​flere kolonner i en tabel i SQL Server 2005?

  4. Opdater erklæring ved hjælp af med klausul