Estoy usando bulk_create para cargar miles o filas en una base de datos postgresql. Desafortunadamente, algunas de las filas están causando IntegrityError y deteniendo el proceso bulk_create. Me preguntaba si había alguna manera de decirle a django que ignorara esas filas y ahorrara la mayor cantidad de lotes posible.Django bulk_create con ignorar las filas que causan IntegrityError?
Respuesta
(Nota: Yo no uso de Django, lo que puede haber respuestas-marco específico más adecuados)
No es posible que Django para hacer esto simplemente ignorando INSERT
fracasos porque PostgreSQL aborta la transacción completa en el primer error
Django necesitaría uno de estos enfoques:
INSERT
cada fila de una transacción separada e ignorar errores (muy lento);- Crea un
SAVEPOINT
antes de cada inserción (puede tener problemas de escala); - Utilice un procedimiento o consulta para insertar solo si la fila no existe (complicada y lenta); o
- Bulk-insert o (mejor)
COPY
los datos en una tablaTEMPORARY
, luego fusione eso en la tabla principal del servidor.
El enfoque tipo upsert (3) parece una buena idea, pero upsert and insert-if-not-exists are surprisingly complicated.
En lo personal, me gustaría tener (4): Me-mayor inserción en una nueva tabla por separado, probablemente UNLOGGED
o TEMPORARY
, entonces me gustaría correr algún manual para SQL:
LOCK TABLE realtable IN EXCLUSIVE MODE;
INSERT INTO realtable
SELECT * FROM temptable WHERE NOT EXISTS (
SELECT 1 FROM realtable WHERE temptable.id = realtable.id
);
El LOCK TABLE ... IN EXCLUSIVE MODE
impide que una inserción concurrente que crea una fila provoque un conflicto con una inserción realizada por la declaración anterior y que falla. Sí no previene SELECT
concurrentes, solo SELECT ... FOR UPDATE
, INSERT
, UPDATE
y DELETE
, por lo que las lecturas de la tabla continúan normalmente.
Si no puede permitirse el lujo de bloquear las escrituras simultáneas durante demasiado tiempo, podría utilizar un CTE escribible para copiar rangos de filas de temptable
en realtable
, reintentando cada bloque si fallara.
Gracias @ craig-ringer Terminé limpiando mi lista de objetos python antes de insertarlos en el DB, algo similar al enfoque # 3 de ustedes, pero en python puro. – Meitham
Hay un ejemplo detallado de (4) en https://rodmtech.net/docs/django/django-bulk_create-without-integrityerror-rollback/ – eugene
O 5. divide y vencerás
no he probado o punto de referencia de este fondo, pero se lleva a cabo bastante bien para mí. YMMV, dependiendo en particular de cuántos errores espera obtener en una operación masiva.
def psql_copy(records):
count = len(records)
if count < 1:
return True
try:
pg.copy_bin_values(records)
return True
except IntegrityError:
if count == 1:
# found culprit!
msg = "Integrity error copying record:\n%r"
logger.error(msg % records[0], exc_info=True)
return False
finally:
connection.commit()
# There was an integrity error but we had more than one record.
# Divide and conquer.
mid = count/2
return psql_copy(records[:mid]) and psql_copy(records[mid:])
# or just return False
Una solución rápida y sucia para esto que no involucra SQL manual y tablas temporales es simplemente intentar insertar de forma masiva los datos. Si falla, vuelva a la inserción en serie.
objs = [(Event), (Event), (Event)...]
try:
Event.objects.bulk_create(objs)
except IntegrityError:
for obj in objs:
try:
obj.save()
except IntegrityError:
continue
Si usted tiene montones y montones de errores esto puede no ser tan eficiente (que va a pasar más tiempo en serie inserción que hacerlo a granel), pero estoy trabajando a través de un conjunto de datos de alta cardinalidad con pocas se duplica así que esto resuelve la mayoría de mis problemas.
Incluso en Django 1.11 no hay forma de hacer esto.Encontré una mejor opción que usar Raw SQL. Lo usa djnago-query-builder. Tiene un método upsert
from querybuilder.query import Query
q = Query().from_table(YourModel)
# replace with your real objects
rows = [YourModel() for i in range(10)]
q.upsert(rows, ['unique_fld1', 'unique_fld2'], ['fld1_to_update', 'fld2_to_update'])
Nota: La biblioteca sólo el soporte de PostgreSQL
- 1. Django 1.4 - bulk_create con una lista
- 2. django Postgres IntegrityError
- 3. ¿Usando Django bulk_create objetos en claves foráneas?
- 4. IntegrityError al cargar accesorio durante las pruebas de Django
- 5. ignorar inserción de filas que infringen índice de clave duplicada
- 6. IntegrityError valor clave duplicado viola la restricción única - django/postgres
- 7. Django seleccionar sólo las filas con valores de campo duplicados
- 8. salida PHPUnit que causan excepciones Zend_Session
- 9. Continuar cargando después de IntegrityError
- 10. Knockout con ko.toJSON - cómo ignorar las propiedades que son nulas
- 11. Django Tabla con millones de filas
- 12. Odd IntegrityError en MySQL: # 1452
- 13. ¿Qué sucede con las filas ignoradas cuando se selecciona Ignorar falla en SSIS?
- 14. IntegrityError: violación de clave externa al eliminar
- 15. JavaScript/Google Maps que causan errores PIE.htc
- 16. Ignorar dinámicamente las propiedades con JacksonJson
- 17. KnockoutJs con Jquery.tablesorter - hacer que las filas de duplicado
- 18. SQLAlchemy IntegrityError e importaciones de datos masivos
- 19. ¿Cómo puedo decirle a las plantillas de Django que no analicen un bloque que contiene código que se parece a las etiquetas de plantilla?
- 20. Ignorar permanentemente las advertencias
- 21. Sql selecciona las filas que contienen parte de la cadena
- 22. Caracteres extraños en Javascript que causan que no se cargue
- 23. ¿Puede Roxygen ignorar las funciones que no son del usuario?
- 24. UITableView que muestra más filas que las especificadas en numberOfRowsInSection:
- 25. ¿Cómo ignorar las líneas que comienzan con una cadena con diff?
- 26. ¿Cómo arreglo IntegrityError con el modelo UserProfile (cuando el usuario creó desde la interfaz de administración)?
- 27. URL Django que no coincida con las variables GET
- 28. Hacer que las URL de Django funcionen con o sin/
- 29. Django problemas con las contraseñas
- 30. Problema al cargar Django accesorio: IntegrityError: (1062, "entrada Duplicar '4' de 'user_id' llave")
que puede no ser posible debido a PostgreSQL aborta la transacción en el primer error. Django necesitaría (a) crear un SAVEPOINT antes de cada inserción, lo que ralentiza las cosas y cuesta recursos; o (b) Use un procedimiento o consulta para insertar solo si la fila aún no existe. Personalmente, insertaría de forma masiva en una nueva tabla separada, probablemente 'DESBLOQUEADO' o 'TEMPORAL', luego 'INSERTAR EN SELECCIONABLE' * SITIO DONDE NO EXISTE (SELECCIONAR 1 FROM realtable DONDE temptable.id = realtable.id) ' o similar. –