Datos básicos: elimina todos los objects de un tipo de entidad, es decir, borra una tabla

Esto se ha preguntado antes, pero no se describe ninguna solución que sea lo suficientemente rápida para las necesidades de mi aplicación.

En el protocolo de comunicaciones que hemos configurado, el server envía un nuevo set de todos los clientes cada vez que se realiza una synchronization. Anteriormente, habíamos estado almacenando como un plist. Ahora quiero usar Core Data.

Puede haber miles de inputs. Eliminar cada uno de forma individual lleva mucho time. ¿Hay alguna forma de eliminar todas las filas en una tabla particular en Datos básicos?

delete from customer 

Esta llamada en sqlite ocurre al instante. Revisar cada uno individualmente en Core Data puede llevar 30 segundos en un iPad1.

¿Es razonable cerrar Core Data, es decir, soltar el almacén de persistencia y todos los contexts de objects gestionados, luego caer en sqlite y ejecutar el command delete contra la tabla? No ocurre otra actividad durante este process, por lo que no necesito acceso a otras partes de la database.

Dave DeLong es un experto en, bueno, casi todo, y siento que le digo a Jesús cómo caminar sobre el agua. De acuerdo, su publicación es de 2009, que fue hace mucho time.

Sin embargo, el enfoque en el enlace publicado por Bot no es necesariamente la mejor manera de manejar eliminaciones grandes.

Básicamente, esa publicación sugiere search los ID de object, y luego iterar a través de ellos, llamando a eliminar en cada object.

El problema es que cuando elimina un solo object, también debe manejar todas las relaciones asociadas, lo que podría provocar nuevas capturas.

Por lo tanto, si debe eliminar a gran escala de esta forma, sugiero ajustar su database general para que pueda aislar tablas en almacenes de datos centrales específicos. De esa forma, puede simplemente eliminar toda la tienda y, posiblemente, rebuild los pequeños bits que desea conservar. Ese será probablemente el enfoque más rápido.

Sin embargo, si desea eliminar los objects ellos mismos, debe seguir este patrón …

Elimine sus lotes, dentro de un set de autorelease, y asegúrese de search previamente cualquier relación en cascada. Todo esto, en set, networkingucirá al mínimo la cantidad de veces que debe ir a la database y, por lo tanto, networkingucirá la cantidad de time que lleva realizar su eliminación.

En el enfoque sugerido, que se networkinguce a …

  1. Obtener ObjectIds de todos los objects que se eliminarán.
  2. Realice un iterato por la list y elimine cada object

Si tiene relaciones en cascada, encontrará muchos viajes adicionales a la database, y el IO es realmente lento. Desea minimizar el número de veces que tiene que visitar la database.

Si bien inicialmente puede sonar contrario a la intuición, desea get más datos de los que cree que desea eliminar. La razón es que todos los datos pueden getse de la database en unas pocas operaciones de E / S.

Entonces, en su request de búsqueda, desea establecer …

 [fetchRequest setRelationshipKeyPathsForPrefetching:@[@"relationship1", @"relationship2", .... , @"relationship3"]]; 

donde esas relaciones representan todas las relaciones que pueden tener una regla de eliminación en cascada.

Ahora, cuando se completa su búsqueda, tiene todos los objects que se van a eliminar, además de los objects que se eliminarán como resultado de la eliminación de esos objects.

Si tiene una jerarquía compleja, quiere anticipar lo más posible antes de time. De lo contrario, cuando elimina un object, Core Data tendrá que ir a search cada relación individualmente para cada object para poder gestionar la eliminación de la cascada.

Esto desperdiciará una TONELADA de time, porque hará muchas más operaciones de IO como resultado.

Ahora, después de que su búsqueda se haya completado, se pasa a través de los objects y se eliminan. Para grandes eliminaciones, puede ver una aceleración de un order de magnitud.

Además, si tiene muchos objects, divídalo en múltiples lotes y hágalo dentro de un grupo de lanzamiento automático.

Finalmente, haz esto en un hilo de background por separado, para que tu interfaz de usuario no se pierda. Puede usar un MOC independiente, conectado a un coordinador de tienda persistente, y hacer que el principal administrador de MOC maneje notifications DoSave para eliminar los objects de su context.

Aunque esto parezca un código, tratarlo como pseudo-código …

 NSManagedObjectContext *deleteContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateConcurrencyType]; // Get a new PSC for the same store deleteContext.persistentStoreCoordinator = getInstanceOfPersistentStoreCoordinator(); // Each call to performBlock executes in its own autoreleasepool, so we don't // need to explicitly use one if each chunk is done in a separate performBlock __block void (^block)(void) = ^{ NSFetchRequest *fetchRequest = // // Only fetch the number of objects to delete this iteration fetchRequest.fetchLimit = NUM_ENTITIES_TO_DELETE_AT_ONCE; // Prefetch all the relationships fetchRequest.relationshipKeyPathsForPrefetching = prefetchRelationships; // Don't need all the properties fetchRequest.includesPropertyValues = NO; NSArray *results = [deleteContext executeFetchRequest:fetchRequest error:&error]; if (results.count == 0) { // Didn't get any objects for this fetch if (nil == results) { // Handle error } return; } for (MyEntity *entity in results) { [deleteContext deleteObject:entity]; } [deleteContext save:&error]; [deleteContext reset]; // Keep deleting objects until they are all gone [deleteContext performBlock:block]; }; [deleteContext preformBlock:block]; 

Por supuesto, necesita hacer el event handling errores adecuado, pero esa es la idea básica.

Obtenga en lotes si tiene tantos datos para borrar que afectará la memory. No obtenga todas las properties. Prefetch relaciones para minimizar las operaciones IO. Utilice autoreleasepool para evitar que la memory crezca. Cortar el context. Realice la tarea en un hilo de background.

Si tiene un gráfico realmente complejo, asegúrese de search previamente todas las relaciones en cascada para todas las entidades en el gráfico de objects completo.

Tenga en count que su context principal tendrá que manejar las notifications de DidSave para mantener su context al día con las eliminaciones.

EDITAR

Gracias. Muchos buenos puntos Todo bien explicado, excepto, ¿por qué crear el MOC por separado? ¿Alguna idea de no eliminar toda la database, sino usar sqlite para eliminar todas las filas de una tabla en particular? – David

Utiliza un MOC por separado para que la interfaz de usuario no se bloquee mientras la operación de eliminación larga está sucediendo. Tenga en count que cuando ocurre el compromiso real a la database, solo un subprocess puede acceder a la database, por lo que cualquier otro acceso (como la obtención) bloqueará las actualizaciones. Esta es otra razón para dividir la operación de eliminación grande en trozos. Las pequeñas piezas de trabajo proporcionarán una oportunidad para que otros MOC (s) accedan a la tienda sin tener que esperar a que se complete toda la operación.

Si esto causa problemas, también puede implementar queues de prioridad (mediante dispatch_set_target_queue ), pero eso está más allá del scope de esta pregunta.

En cuanto a usar los commands sqlite en la database Core Data, Apple ha dicho repetidamente que es una mala idea y que no debe ejecutar commands SQL directos en un file de database Core Data.


Finalmente, déjame notar esto. En mi experiencia, he descubierto que cuando tengo un grave problema de performance, suele ser el resultado de un layout deficiente o de una implementación inadecuada. Revise su problema y vea si puede networkingiseñar su sistema un poco para acomodar mejor este caso de uso.

Si debe enviar todos los datos, quizás consulte la database en un hilo de background y filtre los nuevos datos para que rompa sus datos en tres sets: objects que necesitan modificación, objects que necesitan borrarse y objects que deben insertse.

De esta forma, solo está cambiando la database donde debe cambiarse.

Si los datos son casi totalmente nuevos cada vez, considere reestructurar su database donde estas entidades tengan su propia database (supongo que su database ya contiene varias entidades). De esa forma, puede simplemente eliminar el file y comenzar de nuevo con una database nueva. Eso es rápido. Ahora, reinsert varios miles de objects no va a ser rápido.

Tienes que administrar las relaciones manualmente, en las tiendas. No es difícil, pero no es automático como las relaciones dentro de la misma tienda.

Si hice esto, primero crearía la nueva database, luego derribaría la existente, la replaceía por la nueva, y luego eliminaría la anterior.

Si solo está manipulando su database a través de este mecanismo por lotes, y no necesita la gestión de charts de objects, entonces quizás desee considerar usar sqlite en lugar de Core Data.

iOS 9 y posterior

Utilice NSBatchDeleteRequest . Probé esto en el simulador en una entidad Core Data con más de 400,000 instancias y la eliminación fue casi instantánea.

 // fetch all items in entity and request to delete them let fetchRequest = NSFetchRequest(entityName: "MyEntity") let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) // delegate objects let myManagedObjectContext = (UIApplication.shanetworkingApplication().delegate as! AppDelegate).managedObjectContext let myPersistentStoreCoordinator = (UIApplication.shanetworkingApplication().delegate as! AppDelegate).persistentStoreCoordinator // perform the delete do { try myPersistentStoreCoordinator.executeRequest(deleteRequest, withContext: myManagedObjectContext) } catch let error as NSError { print(error) } 

Tenga en count que la respuesta a la que @Bot se vincula y que @JodyHagins mencionó también se ha actualizado a este método.

Realmente tu única opción es eliminarlos de forma individual. Hago este método con una tonelada de objects y es bastante rápido. Aquí hay una manera en que alguien lo hace solo cargando la ID de object gestionado, de modo que impide cualquier sobrecarga innecesaria y la hace más rápida.

Datos básicos: la forma más rápida de eliminar todas las instancias de una entidad

Sí, es razonable eliminar la tienda persistente y comenzar desde cero. Esto sucede bastante rápido. Lo que puede hacer es eliminar el almacén persistente (con la URL de tienda persistente) del coordinador de tienda persistente, y luego usar la url de la tienda persistente para eliminar el file de la database de su carpeta de directory. Lo hice usando removeItemAtURL de NSFileManager.

Editar: una cosa a tener en count: Asegúrese de deshabilitar / liberar la instancia actual de NSManagedObjectContext y detener cualquier otro subprocess que pueda estar haciendo algo con un NSManagedObjectContext que esté usando el mismo almacén persistente. Su aplicación se bloqueará si un context intenta acceder a la tienda persistente.