Какой самый быстрый способ загрузить большой файл CSV в основные данные

Conclusion
Проблема закрыта, я думаю.
Похоже, что проблема не имеет ничего общего с методологией, но XCode не правильно очистил проект между сборками.
Похоже, что после всех этих тестов файл sqlite, который использовался, все еще был первым, который не был проиндексирован ......
Остерегайтесь XCode 4.3.2, у меня нет ничего, кроме проблем с очисткой, а не очисткой или добавлением файлов в проект, которые не добавляются автоматически в ресурсы пакета ...
Спасибо за разные ответы ..

Update 3
Поскольку я приглашаю кого-нибудь просто попробовать те же шаги, чтобы увидеть, получают ли они одинаковые результаты, позвольте мне подробно рассказать, что я сделал:
Я начинаю с пустого проекта
Я определил модель данных с одной сущностью, 3 атрибутами (2 строки, 1 с плавающей точкой)
Первая строка проиндексирована
enter image description here

В самом деле Finish LaunchingWithOptions я звоню:

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

Код для populateDb ниже:

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

Все остальное - код данных ядра по умолчанию, ничего не добавлено.
Я запускаю это в симуляторе.
Я захожу в ~ / Библиотека / Поддержка приложений / iPhone Simulator / 5.1 / Приложения // Документы
Существует файл sqlite, который создается

Я беру это и копирую в свой комплект

Я закомментирую вызов populateDb

Я редактирую persistentStoreCoordinator, чтобы скопировать файл sqlite из пакета в документы при первом запуске

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


Я удаляю приложение из симулятора и проверяю, что ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications / теперь удалено
Я перестраиваюсь и запускаю снова
Как и ожидалось, файл sqlite копируется в ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications // Documents

However the size of the file is smaller than in the bundle, significantly! Also, doing a simple query with a predicate like this predicate = [NSPredicate predicateWithFormat:@"string1 == %@", string1]; clearly shows that string1 is not indexed anymore

После этого я создаю новую версию модели данных с бессмысленным обновлением, просто чтобы облегчить миграцию
При запуске на симуляторе миграция занимает несколько секунд, размер базы данных увеличивается вдвое, и этот же запрос теперь занимает меньше секунды, а не минуты.
Это решило бы мою проблему, вызвало бы миграцию, но та же самая миграция занимает 3 минуты на iPad и происходит на переднем плане.
Поэтому, где я сейчас нахожусь, лучшим решением для меня все равно было бы предотвращение удаления индексов, а любое другое решение для импорта во время запуска просто занимает слишком много времени.
Дайте мне знать, если вам нужно больше разъяснений ...

Update 2
Таким образом, лучший результат, который у меня был до сих пор, - заполнить базу данных основных данных файлом sqlite, созданным из быстрого инструмента с аналогичной моделью данных, но без индексов, заданных при создании файла sqlite. Затем я импортирую этот sqlite-файл в приложение основных данных с установленными индексами и с учетом облегченной миграции. Для 2 миллионов записей на новом iPad эта миграция займет 3 минуты. Конечное приложение должно иметь 5-кратное количество записей, поэтому мы по-прежнему смотрим на длительное время обработки. Если я пойду по этому пути, то возникнет новый вопрос: можно ли выполнить легкую миграцию в фоновом режиме?

Update
Мой вопрос НЕ в том, как создать инструмент для заполнения базы данных Core Data, а затем импортировать файл sqlite в мое приложение.
 Я знаю, как это сделать, я делал это бесчисленное количество раз.
 Но до сих пор я не осознавал, что такой метод может иметь некоторый побочный эффект: в моем случае индексированный атрибут в результирующей базе данных явно получил «неиндексированный». при импорте файла sqlite таким способом.
Если вы смогли проверить, что какие-либо проиндексированные данные все еще индексируются после такой передачи, мне было бы интересно узнать, как вы поступите, или в противном случае, какова была бы лучшая стратегия для эффективного заполнения такой базы данных.

Original

У меня есть большой файл CSV (миллионы строк) с 4 столбцами, строками и числами с плавающей запятой. Это для iOS-приложения.

Мне нужно, чтобы это было загружено в основные данные при первой загрузке приложения.

Приложение практически не работает до тех пор, пока данные не станут доступны, поэтому время загрузки имеет значение, поскольку первый пользователь, очевидно, не хочет, чтобы приложение загружалось 20 минут, прежде чем оно сможет его запустить.

Прямо сейчас мой текущий код занимает 20 минут на новом iPad, чтобы обработать файл CSV длиной в 2 миллиона строк.

Я использую фоновый контекст, чтобы не блокировать пользовательский интерфейс и сохранять контекст каждые 1000 записей

Первой идеей было создать базу данных на симуляторе, а затем скопировать / вставить ее в папку документа при первом запуске, поскольку это обычный неофициальный способ заполнения большой базы данных. К сожалению, индексы, похоже, не выдерживают такой передачи, и, хотя база данных стала доступна всего через несколько секунд, производительность ужасна, потому что мои индексы были потеряны. Я уже опубликовал вопрос об индексах, но, похоже, на этот вопрос нет хорошего ответа.

Итак, что я ищу, либо:

a way to improve performance on loading millions of records in core data if the database is pre-loaded and moved at first startup, a way to keep my indexes best practices for handling this kind of scenario. I don't remember using any app that requires me to wait for x minutes before first use (but maybe The Daily, and that was a terrible experience). Any creative way to make the user wait without him realizing it: background import while going through tutorial, etc... Not Using Core Data? ...

Ответы на вопрос(2)

Ваш ответ на вопрос