sql >> Base de Datos >  >> RDS >> PostgreSQL

Necesita crear un disparador que incremente un valor en una tabla después de la inserción

Mantener el valor de resumen es complicado:es fácil crear la posibilidad de deadlock tu programa.

Si realmente tiene que hacer esto, porque sabe que de lo contrario tendrá problemas de rendimiento (como nhunts en cientos o más), entonces es mejor crear una tabla de resumen separada para nhunts, algo como:

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

El desencadenante de las cacerías:

  • se ejecuta para cada fila agregada, eliminada y actualizada;
  • añade una fila (id_h, nhunts) = (NEW.id_h, 1) en cada inserto;
  • añade una fila (id_h, nhunts) = (OLD.id_h, -1) en cada eliminación;
  • los dos anteriores en la actualización que cambia id_h .

Como el disparador solo agregará nuevas filas, no bloquea las filas existentes y, por lo tanto, no puede interbloquearse.

Pero esto no es suficiente:como se describió anteriormente, la tabla de resumen hará crecer las filas tan rápido o más rápido que la tabla de búsquedas, por lo que no es muy útil. Por lo tanto, debemos agregar alguna forma de fusionar las filas existentes periódicamente, alguna forma de cambiar:

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

Para:

id_h nhunts
1    3
2    2

Esto no debería ejecutarse en cada invocación de activación, ya que será bastante lento, pero puede ejecutarse aleatoriamente, por ejemplo, cada 1/1024 invocaciones al azar. Esta función utilizará la palabra clave "omitir bloqueo" para evitar tocar filas ya bloqueadas, evitando de otro modo un posible punto muerto.

Dicho activador se vería así:

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

El gatillo funciona lo suficientemente rápido en mi computadora portátil para insertar 1 millón de filas en la tabla de búsqueda en 45 segundos.

Esta vista a continuación facilitará la extracción de nhunts actuales del resumen. Consultarlo tomará un número pequeño o ms, incluso si la tabla de búsquedas estará en miles de millones:

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