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

Musisz utworzyć wyzwalacz, który zwiększa wartość w tabeli po wstawieniu

Utrzymanie wartości podsumowania jest trudne - łatwo jest stworzyć możliwość zakleszczenia Twój program.

Jeśli naprawdę musisz to zrobić, ponieważ wiesz, że w przeciwnym razie będziesz mieć problemy z wydajnością (np. nhunty na setki lub więcej), lepiej utworzyć osobną tabelę podsumowującą dla nhuntów, na przykład:

CREATE TABLE hunts_summary
(
    id_hs bigserial primary key,
    id_h integer NOT NULL,
    nhunts integer NOT NULL
);
CREATE INDEX hunts_summary_id_h_idx on hunts_summary(id_h);

Spust do polowań:

  • działa dla każdego dodanego, usuniętego, zaktualizowanego wiersza;
  • dodaje wiersz (id_h, nhunts) = (NEW.id_h, 1) na każdej wstawce;
  • dodaje wiersz (id_h, nhunts) = (OLD.id_h, -1) przy każdym usunięciu;
  • obie powyższe w aktualizacji, która zmienia id_h .

Ponieważ wyzwalacz doda tylko nowe wiersze, nie blokuje istniejących wierszy i dlatego nie może się zakleszczyć.

Ale to nie wystarczy - jak opisano powyżej, tabela podsumowująca będzie rosła tak szybko lub szybciej niż tabela polowań, więc nie jest zbyt pomocna. Musimy więc dodać jakiś sposób na okresowe scalanie istniejących wierszy - jakiś sposób na zmianę:

id_h nhunts
1    1
1    1
2    1
2    -1
1    1
1    -1
2    1
1    1
2    1

Do:

id_h nhunts
1    3
2    2

Nie powinno to działać przy każdym wywołaniu wyzwalacza, ponieważ będzie wtedy dość powolne, ale może działać losowo - na przykład co 1/1024 wywołanie losowo. Ta funkcja użyje słowa kluczowego „pomiń zablokowane”, aby uniknąć dotykania już zablokowanych wierszy, unikając w przeciwnym razie możliwego zakleszczenia.

Taki wyzwalacz wyglądałby mniej więcej tak:

create or replace function hunts_maintain() returns trigger
as $hunts_maintain$
        begin
                if (tg_op = 'INSERT') then
                        insert into hunts_summary(id_h, nhunts)
                                values (NEW.id_h, 1);
                elsif (tg_op = 'DELETE') then
                        insert into hunts_summary(id_h, nhunts)
                                values (OLD.id_h, -1);
                elsif (tg_op = 'UPDATE' and NEW.id_h!=OLD.id_h) then
                        insert into hunts_summary(id_h, nhunts)
                                values (OLD.id_h, -1), (NEW.id_h, 1);
                end if;

                if (random()*1024 < 1) then
                        with deleted_ids as (
                                select id_hs from hunts_summary for update skip locked
                        ),
                        deleted_nhunts as (
                                delete from hunts_summary where id_hs in (select id_hs from deleted_ids) returning id_h, nhunts
                        )
                        insert into hunts_summary (id_h, nhunts) select id_h, sum(nhunts) from deleted_nhunts group by id_h;
                end if;

                return NEW;
        end;
$hunts_maintain$ language plpgsql;

create trigger hunts_maintain
        after insert or update or delete on hunts
        for each row execute procedure hunts_maintain();

Wyzwalacz działa wystarczająco szybko na moim laptopie, aby wstawić 1 mln wierszy do tabeli polowań w ciągu 45 sekund.

Poniższy widok ułatwi wyodrębnienie bieżących nhuntów z podsumowania. Zapytanie zajmie niewielką liczbę lub ms, nawet jeśli tabela polowań będzie liczona w miliardach:

create or replace view hunts_summary_view as
        select id_h, sum(nhunts) as nhunts
        from hunts_summary
        group by id_h;



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Funkcja MAX() w PostgreSQL

  2. Jak wybrać więcej niż 1 rekord dziennie?

  3. Instalowanie sterowników PDO dla PostgreSQL na Macu (używając Zend dla Eclipse)

  4. NULL utracone elementy podczas rzutowania wyniku unnest()

  5. Jak obsłużyć opcjonalną wartość zwracaną przez zapytanie za pomocą skrzynki postgres?