Reglas de PostgreSQL y NextVal ()/Problema en serie (muy específico de PostgreSQL)

StackOverflow https://stackoverflow.com/questions/1324377

  •  19-09-2019
  •  | 
  •  

Pregunta

Cuando uso una regla de reescritura que divide un inserto en una tabla en insertos en otras dos tablas donde uno de los valores insertados tiene como NextVal predeterminado ('Some_Sequence') con la misma secuencia para ambas tablas, entonces los valores predeterminados insertados son diferentes en las dos tablas. Esto probablemente se deba al simple reemplazo de texto por la regla de reescritura. En cambio, esperaba que el valor predeterminado se resolviera primero y luego se escribiera el mismo valor en ambas tablas.

Aquí un ejemplo (como probablemente adivine, estoy tratando de implementar especialización/generalización utilizando reglas):

-- first and third commands can be skipped if id is defined as serial
create sequence parents_id_seq;
create table Parents(
  id integer default(nextval('parents_id_seq')) primary key,
  type varchar(50) not null check(type in ('Child1', 'Child2')),
  unique (id, type),
  attribute1 varchar(50) not null unique check(length(attribute1) > 0)
);
alter sequence parents_id_seq owned by parents.id;

Los datos específicos de los niños del primer tipo se mantienen en

create table Partial_Children1(
  id integer default(nextval('parents_id_seq')) primary key,
  type varchar(50) not null check(type = 'Child1'),
  foreign key (id, type) references Parents(id, type),
  attribute2 varchar(50) not null check(length(attribute2) > 0)
);

A continuación, definí una vista a Children1 que se une a ambas tablas anteriores (reescribí la vista al declarar explícitamente lo que PostgreSQL hace para definir las vistas de acuerdo con la documentación)

create table Children1(
  id int default(nextval('parents_id_seq')),
  type varchar(50) not null check(type in ('Child1')),
  attribute1 varchar(50) not null check(length(attribute1) > 0),
  attribute2 varchar(50) not null check(length(attribute2) > 0)
);
create rule "_RETURN" as on select to Children1 do instead
  select p.*, c.attribute2
  from Parents p
    join Partial_Children1 c
      on p.id = c.id;

Finalmente la regla de reescritura con la que tengo problemas:

create rule ct_i_children1 as
  on insert to Children1
  do instead (
    insert into Parents(attribute1, type)
      values(new.attribute1, 'Child1');
    insert into Partial_Children1(attribute2, type)
      values(new.attribute2, 'Child1');
  );

Intentando insertar datos con

insert into Children1 (attribute1, attribute2)
  values ('a1', 'a2'),
         ('b1', 'b2');

produce el mensaje de error

ERROR:  insert or update on table "partial_children1" violates foreign key constraint "partial_children1_id_fkey"
DETAIL:  Key (id,type)=(3,Child1) is not present in table "parents".

Una forma de resolver esto es reemplazar el segundo inserto de la regla de reescritura por

insert into Partial_Children1(id, attribute2, type)
  select p.id, new.attribute2, p.type
    from Parents p
    where p.attribute1 = new.attribute1

Pero esto se basa en la singularidad de Attribute1, que no quiero imponer. Otra solución sería insertar los valores primero en una tabla temporal, y luego seleccionar dos veces desde allí para las inserciones en las dos tablas. Pero no me gusta por razones de rendimiento.

¿Alguien tiene otra idea de cómo obtener los mismos valores predeterminados en ambas tablas (solo usando reglas y no desencadenantes)?

¿Fue útil?

Solución

De los documentoshttp://www.postgresql.org/docs/8.4/static/rules.html

(El sistema de reglas) modifica las consultas para tener en cuenta las reglas, y luego pasa la consulta modificada al planificador de consulta para la planificación y la ejecución

Entonces primero reescribe las consultas sin ejecutar nada.

Puede hacer que funcione cuando no inserta registros multipados a la vez:

create or replace rule ct_i_children1 as
  on insert to Children1
  do instead (
    insert into Parents(id, attribute1, type)
      values(nextval('parents_id_seq'), new.attribute1, 'Child1');
    insert into Partial_Children1(id, attribute2, type)
      values(currval('parents_id_seq'), new.attribute2, 'Child1');
  );

Entonces puedes hacer:

insert into Children1 (attribute1, attribute2) values ('a1', 'a2');
insert into Children1 (attribute1, attribute2) values ('b1', 'b2');

pero no

insert into Children1 (attribute1, attribute2)
  values ('a1', 'a2'),
         ('b1', 'b2');

Por lo tanto, realmente no debe usar el sistema de reglas con llamadas difíciles de curval ().

Además, eche un vistazo a los comentarios en estas páginas:

Otro consejo: ¡el soporte en la lista de correo PostgreSQL es tan excelente como el motor de la base de datos en sí!

Y por cierto: ¿sabe que PostgreSQL tiene soporte para la herencia fuera de la caja?

Resumen: ¡Debe usar desencadenantes o evitar múltiples insertos de fila!

Otros consejos

Las reglas lo harán por usted: reescriben la consulta antes de que se ejecute.

Mientras tenga una tabla real para la base (Children1), creo que podrá lograr lo mismo con un desencadenante en lugar de una regla.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top