Qual é a maneira mais rápida de carregar um arquivo CSV grande nos dados principais

Conclusão
Problema fechado, eu acho.
Parece que o problema não tem nada a ver com a metodologia, mas que o XCode não limpou o projeto corretamente entre os builds.
Parece que depois de todos esses testes, o arquivo sqlite que estava sendo usado ainda era o primeiro que não foi indexado ......
Cuidado com o XCode 4.3.2, não tenho nada além de problemas com Limpar não limpar ou adicionar arquivos ao projeto que não são automaticamente adicionados aos recursos do pacote ...
Obrigado pelas diferentes respostas ..

Atualização 3
Como convido alguém a tentar os mesmos passos para ver se obtêm os mesmos resultados, deixe-me detalhar o que fiz:
Eu começo com projeto em branco
Eu defini um datamodel com uma entidade, 3 atributos (2 strings, 1 float)
A primeira string é indexada


Em fiz finishLaunchingWithOptions, eu estou chamando:

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

O código para populateDb está abaixo:

<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 o resto é código de dados do núcleo padrão, nada foi adicionado.
Eu corro isso no simulador.
Eu vou para ~ / Library / Application Support / iPhone Simulator / 5.1 / Aplicações // Documentos
Existe o arquivo sqlite que é gerado

Eu pego isso e copio no meu pacote

Eu comento a chamada para populateDb

Eu edito persistentStoreCoordinator para copiar o arquivo sqlite do pacote para documentos na primeira execução

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


Eu removi o aplicativo do simulador, eu verifiquei que ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications / agora é removido
Eu reconstruo e lanço novamente
Como esperado, o arquivo sqlite é copiado para ~ / Library / Application Support / iPhone Simulator / 5.1 / Applications // Documents

No entanto, o tamanho do arquivo é menor do que no pacote, significativamente! Além disso, fazer uma consulta simples com um predicado como este predicado = [NSPredicate predicateWithFormat: @ "string1 ==% @", string1]; mostra claramente que string1 não está mais indexado

Depois disso, criei uma nova versão do modelo de dados, com uma atualização sem sentido, apenas para forçar uma migração leve
Se executada no simulador, a migração leva alguns segundos, o banco de dados dobra de tamanho e a mesma consulta demora menos de um segundo para retornar em vez de minutos.
Isso resolveria meu problema, forçaria uma migração, mas essa mesma migração leva 3 minutos no iPad e acontece em primeiro plano.
Então, onde estou agora, a melhor solução para mim ainda é evitar que os índices sejam removidos. Qualquer outra solução importadora no momento do lançamento leva muito tempo.
Deixe-me saber se você precisar de mais esclarecimentos ...

Atualização 2
Portanto, o melhor resultado que tive até agora é semear o banco de dados de dados do núcleo com o arquivo sqlite produzido a partir de uma ferramenta rápida com modelo de dados similar, mas sem os índices definidos ao produzir o arquivo sqlite. Em seguida, importo esse arquivo sqlite no aplicativo de dados principais com os índices definidos e permitindo uma migração leve. Para 2 milhões de registros no novo iPad, essa migração ainda leva 3 minutos. O aplicativo final deve ter 5 vezes esse número de registros, portanto, ainda estamos analisando um longo tempo de processamento. Se eu seguir esse caminho, a nova pergunta seria: uma migração leve pode ser executada em segundo plano?

Atualizar
Minha pergunta não é como criar uma ferramenta para preencher um banco de dados do Core Data e depois importar o arquivo sqlite para o meu aplicativo.
Eu sei como fazer isso, tenho feito isso inúmeras vezes.
Mas até agora, eu não tinha percebido que tal método poderia ter algum efeito colateral: no meu caso, um atributo indexado no banco de dados resultante ficou claramente 'não-indexado' ao importar o arquivo sqlite dessa maneira.
Se você conseguiu verificar se os dados indexados ainda estão indexados após essa transferência, estou interessado em saber como você procede ou, de outra forma, qual seria a melhor estratégia para propagar esse banco de dados com eficiência.

Original

Eu tenho um grande arquivo CSV (milhões de linhas) com 4 colunas, strings e floats. Isto é para um aplicativo iOS.

Eu preciso que isso seja carregado nos dados principais na primeira vez que o aplicativo for carregado.

O aplicativo é praticamente não funcional até que os dados estejam disponíveis, portanto, o tempo de carregamento é importante, já que um usuário iniciante obviamente não deseja que o aplicativo leve 20 minutos para carregar antes de poder executá-lo.

No momento, meu código atual leva 20 min no novo iPad para processar um arquivo csv de 2 milhões de linhas.

Eu estou usando um contexto de plano de fundo para não bloquear a interface do usuário e salvar o contexto a cada 1.000 registros

A primeira idéia que tive foi gerar o banco de dados no simulador, depois copiá-lo / colá-lo na pasta do documento no primeiro lançamento, pois essa é a maneira comum não oficial de semear um grande banco de dados. Infelizmente, os índices não parecem sobreviver a essa transferência e, embora o banco de dados esteja disponível após alguns segundos, o desempenho é terrível porque meus índices foram perdidos. Eu postei uma pergunta sobre os índices já, mas parece não haver uma boa resposta para isso.

Então, o que eu estou procurando:

uma maneira de melhorar o desempenho ao carregar milhões de registros em dados principaisse o banco de dados for pré-carregado e movido na primeira inicialização, uma maneira de manter meus índicesmelhores práticas para lidar com esse tipo de cenário. Não me lembro de usar qualquer aplicativo que requer que eu espere x minutos antes do primeiro uso (mas talvez o The Daily, e essa foi uma experiência terrível).Qualquer forma criativa de fazer o usuário esperar sem que ele perceba: importação de fundo ao passar pelo tutorial, etc ...Não está usando dados do núcleo?...

questionAnswers(2)

yourAnswerToTheQuestion