2009-08-24 9 views
10

Cuando uso una regla de reescritura que divide una inserción en una tabla en inserciones en otras dos tablas donde uno de los valores insertados tiene como valor predeterminado nextval ('some_sequence ') con la misma secuencia para ambas tablas, los valores predeterminados insertados son diferentes en las dos tablas. Esto es probablemente debido a la sustitución de texto simple 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.Reglas de PostgreSQL y nextval()/problema de serie (muy específico de PostgreSQL)

Aquí un ejemplo (como probablemente adivinar, estoy tratando de poner en práctica la especialización/generalización usando 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 para los niños de la primera clase se mantiene 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) 
); 

Siguiente He definido una vista Children1 que une las dos tablas anteriores (reescribí la vista indicando explícitamente lo que PostgreSQL hace para definir las vistas según 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 que estoy teniendo problemas con:

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

Tratando de 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 manera de resolver este está reemplazando la segunda inserción 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 exclusividad 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 motivos de rendimiento.

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

Respuesta

5

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

Es (el sistema de reglas) modifica consultas tomen reglas en consideración, y pasa entonces a la consulta modificada al planificador consulta para la planificación y ejecución

por lo que primero vuelve a escribir las consultas sin ejecutar nada.

puede hacer que funcione cuando no se inserta registros multipe 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 usted puede 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 que realmente no debería usar el sistema de reglas con llamadas currval() difíciles.

Además echar un vistazo a los comentarios sobre estas páginas:

Otro consejo: el apoyo a 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 desde el primer momento?

Resumen: se debe utilizar disparadores o evitar múltiples inserciones de fila!

+0

Gracias por los enlaces. No resuelven el problema, pero al menos ya no me siento solo ;-). La herencia incorporada no ofrecía exactamente lo que yo quería. –

0

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

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

+0

Disculpa, pero ¿has leído mi pregunta por completo? –

+0

Um, pensé que sí. Pero creo que al menos me perdí la parte de "no desencadenantes", lo siento. –

+0

Y las reglas dan los resultados incorrectos ;-). –