¿Cuál es la forma más rápida de cargar un archivo CSV grande en datos básicos?
Conclusión
Problema cerrado, creo.
Parece que el problema no tuvo nada que ver con la metodología, pero que XCode no limpió el proyecto correctamente entre las compilaciones.
Parece que después de todas esas pruebas, el archivo sqlite que estaba siendo usado era el primero que no estaba indexado ...
Cuidado con XCode 4.3.2, no tengo más que problemas con la limpieza que no limpia, o agregar archivos al proyecto que no se agregan automáticamente a los recursos del paquete ...
Gracias por las diferentes respuestas ..
Actualización 3
Ya que invito a cualquiera a que intente los mismos pasos para ver si obtienen los mismos resultados, permítame detallar lo que hice:
Empiezo con proyecto en blanco
Definí un modelo de datos con una entidad, 3 atributos (2 cadenas, 1 flotante)
La primera cadena está indexada.
En ¿Terminé de lanzar el lanzamiento con opciones? Estoy llamando:
<code>[self performSelectorInBackground:@selector(populateDB) withObject:nil]; </code>
El código para populateDb está abajo:
<code>-(void)populateDB{ NSLog(@"start"); NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; NSManagedObjectContext *context; if (coordinator != nil) { context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator:coordinator]; } NSString *filePath = [[NSBundle mainBundle] pathForResource:@"input" ofType:@"txt"]; if (filePath) { NSString * myText = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; if (myText) { __block int count = 0; [myText enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) { line=[line stringByReplacingOccurrencesOfString:@"\t" withString:@" "]; NSArray *lineComponents=[line componentsSeparatedByString:@" "]; if(lineComponents){ if([lineComponents count]==3){ float f=[[lineComponents objectAtIndex:0] floatValue]; NSNumber *number=[NSNumber numberWithFloat:f]; NSString *string1=[lineComponents objectAtIndex:1]; NSString *string2=[lineComponents objectAtIndex:2]; NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:@"Bigram" inManagedObjectContext:context]; [object setValue:number forKey:@"number"]; [object setValue:string1 forKey:@"string1"]; [object setValue:string2 forKey:@"string2"]; NSError *error; count++; if(count>=1000){ if (![context save:&error]) { NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]); } count=0; } } } }]; NSLog(@"done importing"); NSError *error; if (![context save:&error]) { NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]); } } } NSLog(@"end"); } </code>
Todo lo demás es el código de datos central por defecto, nada añadido.
Lo ejecuto en el simulador.
Voy a ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications // Documents
Existe el archivo sqlite que se genera.
Tomo eso y lo copio en mi paquete
Yo comento la llamada a populateDb
Edito persistentStoreCoordinator para copiar el archivo sqlite del paquete a los documentos en la primera ejecución
<code>- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { @synchronized (self) { if (__persistentStoreCoordinator != nil) return __persistentStoreCoordinator; NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"myProject" ofType:@"sqlite"]; NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: @"myProject.sqlite"]; NSError *error; if (![[NSFileManager defaultManager] fileExistsAtPath:storePath]) { if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error]) NSLog(@"Copied starting data to %@", storePath); else NSLog(@"Error copying default DB to %@ (%@)", storePath, error); } NSURL *storeURL = [NSURL fileURLWithPath:storePath]; __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return __persistentStoreCoordinator; } } </code>
Quito la aplicación del simulador, compruebo que ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications / ahora está eliminado
Reconstruyo y lanzo de nuevo
Como era de esperar, el archivo sqlite se copia en ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications // Documents
Sin embargo, el tamaño del archivo es más pequeño que en el paquete, significativamente! También, haciendo una consulta simple con un predicado como este predicado = [NSPredicate predicateWithFormat: @ "string1 ==% @", string1]; muestra claramente que string1 ya no está indexado
A continuación, creo una nueva versión del modelo de datos, con una actualización sin sentido, solo para forzar una migración liviana
Si se ejecuta en el simulador, la migración demora unos segundos, la base de datos duplica su tamaño y la misma consulta ahora tarda menos de un segundo en regresar en lugar de minutos.
Esto resolvería mi problema, forzaría una migración, pero esa misma migración demora 3 minutos en el iPad y sucede en primer plano.
Así que, de todos modos, la mejor solución para mí sería evitar que se eliminen los índices, cualquier otra solución de importación en el momento del lanzamiento solo lleva demasiado tiempo.
Déjame saber si necesitas más aclaraciones ...
Actualización 2
Entonces, el mejor resultado que he tenido hasta ahora es sembrar la base de datos de datos central con el archivo sqlite producido a partir de una herramienta rápida con un modelo de datos similar, pero sin los índices establecidos al producir el archivo sqlite. Luego, importo este archivo sqlite en la aplicación de datos principales con los índices establecidos, y permitiendo una migración liviana. Para un registro de 2 millones en el nuevo iPad, esta migración aún toma 3 minutos. La aplicación final debe tener 5 veces este número de registros, por lo que todavía estamos buscando un tiempo de procesamiento largo y largo. Si voy por ese camino, la nueva pregunta sería: ¿se puede realizar una migración ligera en segundo plano?
Actualizar
Mi pregunta NO es cómo crear una herramienta para poblar una base de datos de Core Data, y luego importar el archivo sqlite a mi aplicación.
Sé cómo hacerlo, lo he hecho innumerables veces.
Pero hasta ahora, no me había dado cuenta de que tal método podría tener algún efecto secundario: en mi caso, un atributo indexado en la base de datos resultante se 'desindexó' al importar el archivo sqlite de esa manera.
Si pudo verificar que todos los datos indexados aún se indexan después de dicha transferencia, me interesa saber cómo proceder, o de lo contrario, cuál sería la mejor estrategia para generar esa base de datos de manera eficiente.
Original
Tengo un archivo CSV grande (millones de líneas) con 4 columnas, cadenas y flotadores. Esto es para una aplicación de iOS.
Necesito que esto se cargue en los datos principales la primera vez que se carga la aplicación.
La aplicación es prácticamente no funcional hasta que los datos estén disponibles, por lo que el tiempo de carga es importante, ya que, por primera vez, el usuario no quiere que la aplicación tarde 20 minutos en cargarse antes de poder ejecutarla.
En este momento, mi código actual toma 20 minutos en el nuevo iPad para procesar un archivo csv de 2 millones de líneas.
Estoy usando un contexto de fondo para no bloquear la interfaz de usuario y guardar el contexto cada 1,000 registros
La primera idea que tuve fue generar la base de datos en el simulador, luego copiarla y pegarla en la carpeta de documentos en el primer lanzamiento, ya que esta es la forma común no oficial de sembrar una gran base de datos. Desafortunadamente, los índices no parecen sobrevivir a tal transferencia, y aunque la base de datos estaba disponible después de unos pocos segundos, el rendimiento es terrible porque mis índices se perdieron. Ya publiqué una pregunta sobre los índices, pero no parece haber una buena respuesta para eso.
Entonces, lo que estoy buscando, ya sea: