2010-04-21 33 views
33

Tengo una tabla con aproximadamente 5 millones de filas que tiene una restricción fk que hace referencia a la clave principal de otra tabla (también aproximadamente 5 millones de filas).PostgreSQL - deshabilitar las restricciones

Necesito eliminar aproximadamente 75000 filas de ambas tablas. Sé que si trato de hacer esto con la restricción fk activada, tomará una cantidad inaceptable de tiempo.

Viniendo de un fondo de Oracle, mi primer pensamiento fue deshabilitar la restricción, hacer la eliminación & y luego volver a habilitar la restricción. PostGres parece permitirme desactivar los activadores de restricciones si soy un súper usuario (no lo soy, pero estoy iniciando sesión como el usuario que posee/creó los objetos) pero eso no parece ser lo que quiero.

La otra opción es soltar la restricción y luego restablecerla. Me preocupa que la reconstrucción de la restricción demorará años dado el tamaño de mis tablas.

¿Alguna idea?

editar: después del aliento de Billy He intentado hacer la eliminación sin cambiar ninguna restricción y toma más de 10 minutos. Sin embargo, he descubierto que la tabla desde la que intento eliminar tiene una clave externa autorreferencial ... duplicada (& no indexada).

Actualización final - Dejé caer la clave externa autorreferencial, hice mi eliminación y la agregué nuevamente. Billy tiene razón en todo pero desafortunadamente no puedo aceptar su comentario como la respuesta.

+4

Si está tomando tanto tiempo, incluso con 5 millones de filas, luego de que ha configurado algo mal. –

+0

¿Qué? ¿Borrar o volver a habilitar la restricción? Y sí, es posible que algo (s) esté configurado incorrectamente o de una manera menos optimizada: la base de datos ha sido 'construida' por hibernate (no tuve nada que ver con eso). – azp74

+10

La eliminación. Los cheques FK de tablas indexadas toman tiempo lineal y eliminan 75000 + 75000 filas = 150 000 filas. Considere un caso peor 19 comparaciones por cheque FK (búsqueda binaria, lg (5 millones) == 19), y tal vez 20 comparaciones de máquinas por comparación de filas, lo que equivale a 57 000 000 comparaciones. Considerando una estimación conservadora de que la máquina promedio puede hacer mil millones de comparaciones por segundo, es fácil, esto aún debería tomar menos de un segundo de tiempo de CPU. Cargar desde el disco tampoco debería ser un problema importante porque incluso en 5 millones de filas, la tabla debe caber en la RAM. –

Respuesta

42

Según los comentarios anteriores, debería ser un problema. Dicho esto, hay un comando que puede ser lo que estás buscando: establecerá las restricciones a diferidas para que se comprueben en COMMIT, no en cada eliminación. Si haces solo un gran DELETE de todas las filas, no hará la diferencia, pero si lo haces en pedazos, lo hará.

SET CONSTRAINTS ALL DEFERRED 

es lo que está buscando en ese caso. Tenga en cuenta que las restricciones se deben marcar como DEFERRABLE antes de que puedan diferirse. Por ejemplo:

ALTER TABLE table_name 
    ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2) 
    DEFERRABLE INITIALLY IMMEDIATE; 

La restricción puede entonces ser diferido en una operación o función como sigue:

CREATE OR REPLACE FUNCTION f() RETURNS void AS 
$BODY$ 
BEGIN 
    SET CONSTRAINTS ALL DEFERRED; 

    -- Code that temporarily violates the constraint... 
    -- UPDATE table_name ... 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 
+1

Sin duda, vale la pena intentarlo, pero no estoy convencido de que las restricciones diferidas sean más rápidas. AFAIK simplemente cambian el trabajo de validación de DELETE-time a COMMIT-time. – intgr

+1

Hubiera dado una oportunidad, pero dejar el fk y reinstalarlo funcionó. Al igual que intgr, me pregunto si no solo cambiaría la comprobación de la fk para comprometer el tiempo, así que definitivamente lo recordaré la próxima vez. – azp74

+1

Dejé caer una base de datos y la reimporté después de ejecutar 'SET RESTRAINTS ALL DEFERRED'. ¿Hay alguna manera de "volver a habilitar" estas restricciones una vez que se realiza la importación? Es un archivo bastante grande, por lo que sería bastante difícil volver a ordenar la creación de la tabla. Ya solucioné esto importando los datos dos veces. – taco

-7

Disable todas las restricciones de tabla

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName 

- Activar todas las restricciones de tabla

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName 
+3

La pregunta fue sobre Postgresql que no tiene esa capacidad (a partir de v9.4). –

+0

acuerdo v9.4 no tiene esta característica ERROR: error de sintaxis en o cerca de "NOCHECK" LÍNEA 1: ALTER TABLE TableName NOCHECK CONSTREÑIMIENTO ConstraintName –

3

(Esta respuesta asume que su intención es eliminar todas las filas de estas tablas, no solo una selección.)

También tuve que hacer esto, pero como parte de un conjunto de pruebas. Encontré la respuesta, sugerí elsewhere on SO.Utilice TRUNCATE TABLE de la siguiente manera:

TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE]; 

La siguiente rápidamente elimina todas las filas de mesas table1, table2 y table3, siempre y cuando no hay referencias a las filas de estas tablas de las tablas que se señalan:

TRUNCATE TABLE table1, table2, table3; 

Siempre que las referencias se encuentren entre las tablas enumeradas, PostgreSQL eliminará todas las filas sin preocuparse por la integridad referencial. Si una tabla distinta de las enumeradas hace referencia a una fila de una de estas tablas, la consulta fallará.

Sin embargo, se puede calificar la consulta de modo que también trunca todas las tablas con referencias a las tablas que figuran (aunque no he probado este):

TRUNCATE TABLE table1, table2, table3 CASCADE; 

De manera predeterminada, las secuencias de estas tablas no lo hacen reiniciar la numeración. Las nuevas filas continuarán con el siguiente número de la secuencia. Para reiniciar la numeración de secuencia:

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY; 
7

Lo que funcionó para mí fue desactivar uno por uno el TRIGGERS de esas tablas que se van a estar involucrados en la operación DELETE.

ALTER TABLE reference DISABLE TRIGGER ALL; 
DELETE FROM reference WHERE refered_id > 1; 
ALTER TABLE reference ENABLE TRIGGER ALL; 

La solución está funcionando en la versión 9.3.16. En mi caso el tiempo pasó de 45 minutos a 14 segundos ejecutando DELETE operaciones.

Como se indica en la sección de comentarios de @amphetamachine, deberá tener privilegios admin en las tablas para realizar esta tarea.

+1

Tenga en cuenta que el usuario PostgreSQL ejecutando el 'ALTER TABLE' comandos debe ser el propietario de ese mesa. – amphetamachine

0

Si intenta DISABLE TRIGGER ALL y obtener un error como permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger (Tengo esto en Amazon RDS), intente esto:

set session_replication_role to replica; 

Si esto tiene éxito, todos los disparadores que subyacen a las restricciones de tabla serán desactivados. Ahora depende de usted asegurarse de que sus cambios dejen el DB en un estado consistente.

Luego, cuando haya terminado, vuelva a habilitar desencadena & limitaciones para su sesión con:

set session_replication_role to default; 
Cuestiones relacionadas