Was ist der schnellste Weg, um eine große CSV-Datei in die Kerndaten zu laden?

Fazit
Problem geschlossen, denke ich.
Es sieht so aus, als hätte das Problem nichts mit der Methodik zu tun, aber dass der XCode das Projekt zwischen den Builds nicht richtig bereinigt hat.
Nach all diesen Tests war die verwendete SQLite-Datei immer noch die erste, die nicht indiziert war.
Hüten Sie sich vor XCode 4.3.2, ich habe nur Probleme, wenn Clean nicht bereinigt oder Dateien zum Projekt hinzugefügt werden, die nicht automatisch zu den Bundle-Ressourcen hinzugefügt werden ...
Danke für die unterschiedlichen Antworten ..

Update 3
Da ich jeden einlade, dieselben Schritte zu versuchen, um zu prüfen, ob dieselben Ergebnisse erzielt werden, möchte ich Folgendes genauer erläutern:
Ich beginne mit einem leeren Projekt
Ich habe ein Datenmodell mit einer Entität, 3 Attributen (2 Strings, 1 Float) definiert.
Die erste Zeichenfolge ist indiziert


In did finishLaunchingWithOptions rufe ich an:

<code>[self performSelectorInBackground:@selector(populateDB) withObject:nil];
</code>

Der Code für populateDb ist unten:

<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>

Alles andere ist Standard-Kerndatencode, nichts hinzugefügt.
Das starte ich im Simulator.
Ich gehe zu ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications // Documents
Es gibt die SQLite-Datei, die generiert wird

Ich nehme das und kopiere es in mein Bündel

Ich kommentiere den Aufruf von populateDb aus

Ich bearbeite persistentStoreCoordinator, um die SQLite-Datei beim ersten Start aus dem Bundle in Dokumente zu kopieren

<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>


Ich entferne die App aus dem Simulator und überprüfe, ob ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications / jetzt entfernt ist
Ich baue um und starte wieder
Wie erwartet wird die SQLite-Datei nach ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications // Documents kopiert

Allerdings ist die Größe der Datei deutlich kleiner als im Bundle! Durchführen einer einfachen Abfrage mit einem Vergleichselement wie diesem Vergleichselement = [NSPredicate predicateWithFormat: @ "string1 ==% @", string1]; zeigt deutlich, dass string1 nicht mehr indiziert ist

Anschließend erstelle ich eine neue Version des Datenmodells mit einem bedeutungslosen Update, um eine einfache Migration zu erzwingen
Wenn die Migration auf dem Simulator ausgeführt wird, dauert es einige Sekunden, die Datenbank verdoppelt sich und dieselbe Abfrage dauert jetzt weniger als eine Sekunde, um anstelle von Minuten zurückzukehren.
Dies würde mein Problem lösen, eine Migration erzwingen, aber dieselbe Migration dauert 3 Minuten auf dem iPad und findet im Vordergrund statt.
Wenn ich gerade hier bin, besteht die beste Lösung für mich immer noch darin, das Entfernen der Indizes zu verhindern. Jede andere Importlösung zum Startzeitpunkt benötigt einfach zu viel Zeit.
Lassen Sie mich wissen, wenn Sie weitere Erläuterungen benötigen ...

Update 2
Das beste Ergebnis, das ich bisher erzielt habe, ist, die Kerndatenbank mit der SQLite-Datei zu sortieren, die mit einem schnellen Tool mit ähnlichem Datenmodell erstellt wurde, jedoch ohne die bei der Erstellung der SQLite-Datei festgelegten Indizes. Anschließend importiere ich diese SQLite-Datei mit den festgelegten Indizes in die Core Data App und ermögliche eine einfache Migration. Für 2 Millionen Aufnahmen auf dem neuen iPad dauert diese Migration 3 Minuten. Die endgültige App sollte das Fünffache dieser Anzahl von Datensätzen haben, sodass wir immer noch von einer langen langen Verarbeitungszeit ausgehen. Wenn ich diesen Weg gehe, würde die neue Frage lauten: Kann eine Lightweight-Migration im Hintergrund durchgeführt werden?

Aktualisieren
Meine Frage ist NICHT, wie man ein Tool erstellt, um eine Core Data-Datenbank zu füllen, und dann die SQLite-Datei in meine App importiert.
Ich weiß, wie man das macht, ich habe es unzählige Male gemacht.
Bisher war mir jedoch nicht klar, dass eine solche Methode einen Nebeneffekt haben könnte: In meinem Fall wurde ein indiziertes Attribut in der resultierenden Datenbank beim Importieren der SQLite-Datei auf diese Weise eindeutig "nicht indiziert".
Wenn Sie nach einer solchen Übertragung überprüfen konnten, ob alle indizierten Daten noch indiziert sind, sind Sie an der Vorgehensweise oder an der besten Strategie interessiert, um eine solche Datenbank effizient zu erstellen.

Original

Ich habe eine große CSV-Datei (Millionen Zeilen) mit 4 Spalten, Strings und Floats. Dies ist für eine iOS-App.

Ich benötige dies, um beim ersten Laden der App in die Kerndaten geladen zu werden.

Die App ist so gut wie ohne Funktion, bis die Daten verfügbar sind. Daher ist das Laden von Bedeutung, da ein Benutzer zum ersten Mal offensichtlich nicht möchte, dass die App 20 Minuten lang geladen wird, bevor sie ausgeführt werden kann.

Momentan benötigt mein aktueller Code auf dem neuen iPad 20 Minuten, um eine 2-Millionen-Zeilen-CSV-Datei zu verarbeiten.

Ich verwende einen Hintergrundkontext, um die Benutzeroberfläche nicht zu sperren, und speichere den Kontext alle 1.000 Datensätze

Die erste Idee, die ich hatte, war, die Datenbank auf dem Simulator zu generieren und sie dann beim ersten Start zu kopieren / in den Dokumentordner einzufügen, da dies der übliche nicht offizielle Weg ist, eine große Datenbank zu erstellen. Leider scheinen die Indizes eine solche Übertragung nicht zu überstehen, und obwohl die Datenbank bereits nach wenigen Sekunden verfügbar war, ist die Leistung schrecklich, da meine Indizes verloren gegangen sind. Ich habe bereits eine Frage zu den Indizes gestellt, aber es scheint keine gute Antwort darauf zu geben.

Also, wonach ich suche, entweder:

Eine Möglichkeit, die Leistung beim Laden von Millionen von Datensätzen in Kerndaten zu verbessernWenn die Datenbank beim ersten Start vorinstalliert und verschoben wurde, können meine Indizes beibehalten werdenBest Practices für den Umgang mit solchen Szenarien. Ich kann mich nicht erinnern, eine App verwendet zu haben, bei der ich vor der ersten Verwendung x Minuten warten muss (aber vielleicht The Daily, und das war eine schreckliche Erfahrung).Jede kreative Möglichkeit, den Benutzer warten zu lassen, ohne dass er es merkt: Hintergrundimport während des Lernprogramms usw.Verwenden Sie keine Kerndaten?...

Antworten auf die Frage(2)

Ihre Antwort auf die Frage