La forma más eficiente de eliminar filas en masa de postgres


23

Me pregunto cuál sería la forma más eficiente de eliminar grandes cantidades de filas de PostgreSQL, este proceso sería parte de una tarea recurrente todos los días para importar datos en masa (un delta de inserciones + eliminaciones) en una tabla. Podría haber miles, potencialmente millones de filas para eliminar.

Tengo un archivo de claves principales, una por línea. Las dos opciones en las que estaba pensando estaban en la línea de abajo, pero no conozco / entiendo lo suficiente de lo interno de PostgreSQL para tomar una decisión informada que sería lo mejor.

  • Ejecute una DELETEconsulta para cada fila en el archivo, con una simple WHEREclave primaria (o agrupe las eliminaciones en lotes para nusar una IN()cláusula)
  • Importe las claves primarias en una tabla temporal con el COPYcomando y luego elimínelas de la tabla principal con una combinación

¡Cualquier sugerencia será muy apreciada!


1
La misma pregunta se ha respondido con más detalle aquí: stackoverflow.com/a/8290958
Simon

Respuestas:


25

Su segunda opción es mucho más limpia y funcionará lo suficientemente bien como para que valga la pena. Su alternativa es crear consultas gigantescas que serán bastante difíciles de planificar y ejecutar. En general, será mejor dejar que PostgreSQL haga el trabajo aquí. En general, he encontrado actualizaciones en decenas de miles de filas en la forma en que estás describiendo que funcionan adecuadamente, pero hay una cosa importante que debes evitar.

La forma de hacerlo es usar una selección y una combinación en su eliminación.

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

Bajo ninguna circunstancia debe hacer lo siguiente con una tabla grande:

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

Esto generalmente causará un bucle anidado antiunión que hará que el rendimiento sea bastante problemático. Si terminas teniendo que ir por esa ruta, haz esto en su lugar:

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQL generalmente es bastante bueno para evitar malos planes, pero todavía hay casos que involucran uniones externas que pueden hacer una gran diferencia entre los buenos y los malos planes.

Esto está vagando un poco más lejos, pero creo que vale la pena mencionarlo por lo fácil que es pasar de IN a NOT IN y ver el tanque de rendimiento de consultas.


Eso ayudó mucho, gracias! Sin embargo, descubrí que el uso de "consultas combinadas" es más eficiente en este caso particular. Por ejemplo, IN ( select id from foo except select id from rows_to_keep ) ver postgresql.org/docs/9.4/static/queries-union.html
Ufos el

1

Encontré esta pregunta porque tenía un problema similar. Estoy limpiando una base de datos que tiene más de 300 millones de filas, la base de datos final solo tendrá alrededor del 30% de los datos originales. Si se enfrenta a un escenario similar, en realidad es más fácil insertar en una nueva tabla y volver a indexar en lugar de eliminar.

Hacer algo como

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

Con una indexación adecuada en foo y bar, puede evitar los escaneos Seq.

Entonces tendría que volver a indexar y cambiar el nombre de la tabla.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.