2011-03-11 10 views
8

(PostgreSQL) que estaba tratando de COPY datos CSV en una mesa, pero me estaba duplicado errores clave violación, y no hay manera de saber COPY hacer caso omiso de ellos, así que siguiendo la sabiduría Internet He intentado añadir esta regla:¿Por qué esta regla no evita las violaciones de clave duplicadas?

CREATE OR REPLACE RULE ignore_duplicate_inserts AS 
    ON INSERT TO mytable 
    WHERE (EXISTS (SELECT mytable.id 
      FROM mytable 
      WHERE mytable.id = new.id)) DO NOTHING; 

para eludir el problema, pero sigo recibiendo esos errores, ¿alguna idea de por qué?

Respuesta

10

reglas por defecto: add things to the current action

En términos generales, una regla hace que los comandos adicionales que se ejecutarán cuando se ejecuta una orden dada en una tabla dada.

Pero una regla VEZ le permite reemplazar la acción:

Alternativamente, una regla en su lugar puede sustituir a una orden dada por otro, o causar una orden de no ser ejecutado en absoluto.

Por lo tanto, creo que quiere to specify INSTEAD:

CREATE OR REPLACE RULE ignore_duplicate_inserts AS 
    ON INSERT TO mytable 
    WHERE (EXISTS (SELECT mytable.id 
      FROM mytable 
      WHERE mytable.id = new.id)) DO INSTEAD NOTHING; 

Sin el cambio, su regla es esencialmente diciendo "el inserto y luego no hacer nada" cuando se quiere decir "en lugar del inserto, no hacer nada "y, AFAIK, el DO INSTEAD NOTHING hará eso.

No soy un experto en reglas de PostgreSQL, pero creo que agregar el "INSTEAD" debería funcionar.

ACTUALIZACIÓN: Gracias a araqnid we know that:

COPIA DE invocará cualquier disparador y comprobar las restricciones en la tabla de destino. Sin embargo, no invocará las reglas

De modo que una regla no funcionará en esta situación. Sin embargo, los disparadores se activan durante COPIA DE por lo que podría escribir una antes de insertar trigger that would return NULL cuando se detecta filas duplicadas:

puede devolver NULL para omitir la operación de la fila actual. Esto instruye al ejecutor a no realizar la operación de nivel de fila que invocó el desencadenador (la inserción o modificación de una fila de tabla particular).

Dicho esto, creo que estaría mejor con araqnid de "cargar todo en una tabla temporal, limpiarlo, y copiarlo en el destino final" sería una solución más sensata para una carga a granel operación como tú.

+2

Esto funcionaría para las instrucciones INSERT reales, pero no COPIA. – araqnid

+1

@araqnid: "COPY FROM invocará cualquier activador y verificará las restricciones en la tabla de destino. Sin embargo, no invocará reglas". Sin embargo, podría usar un desencadenante BEFORE INSERT y devolver NULL para omitir los duplicados. OTOH, los disparadores probablemente no sean la mejor herramienta para lidiar con la carga masiva de datos. –

5

COPY FROM no invocará reglas (http://www.postgresql.org/docs/9.0/interactive/sql-copy.html#AEN58860)

Mi enfoque sería para cargar los datos CSV en una temp tabla, luego use una declaración INSERT...SELECT para copiar los datos en la tabla de destino donde aún no existe. (Si hay duplicados en los datos CSV en sí, elimínelos de la tabla temporal primero).Algo como:

BEGIN; 
CREATE TEMP TABLE stage_data(key_column, data_columns...) ON COMMIT DROP; 
\copy stage_data from data.csv with csv header 
-- prevent any other updates while we are merging input (omit this if you don't need it) 
LOCK target_data IN SHARE ROW EXCLUSIVE MODE; 
-- insert into target table 
INSERT INTO target_data(key_column, data_columns...) 
    SELECT key_column, data_columns... 
    FROM stage_data 
    WHERE NOT EXISTS (SELECT 1 FROM target_data 
        WHERE target_data.key_column = stage_data.key_column) 
END; 
+0

"select distinct" para alimentar las inserciones funcionaría, excepto que los duplicados ya podrían estar en target_data. Para limpiar un conjunto de datos a la vez, funcionaría. –

Cuestiones relacionadas