ios - 将大型CSV文件加载到核心数据中最快的方法是什么

标签 ios sqlite core-data csv

结论
我认为问题已经解决。
看起来问题与方法无关,但是XCode在两次构建之间没有正确清理项目。
经过所有这些测试之后,看起来正在使用的sqlite文件仍然是第一个未索引的文件……
提防XCode 4.3.2,除了清理不清理问题外,我什么都没有,或者将文件添加到项目中不会自动添加到捆绑资源中...
感谢您的不同答案。

更新3
由于我邀请任何人尝试相同的步骤,看看它们是否获得相同的结果,因此,让我详细说明一下我所做的事情:
我从空白项目开始
我定义了一个具有1个实体,3个属性(2个字符串,1个浮点数)的数据模型
第一个字符串被索引

在完成finishLaunchingWithOptions中,我正在调用:

[self performSelectorInBackground:@selector(populateDB) withObject:nil];

populateDb的代码如下:
-(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");
}

其他所有内容都是默认的核心数据代码,未添加任何内容。
我在模拟器中运行它。
我转到〜/Library/Application Support/iPhone Simulator/5.1/Applications//Documents
有生成的sqlite文件
我拿走了,然后将其复制到我的捆绑包中

我注释掉对populateDb的调用
我编辑persistentStoreCoordinator,以便在首次运行时将sqlite文件从捆绑包复制到文档中
- (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;
}    
}

我从模拟器中删除了该应用程序,并检查〜/Library/Application Support/iPhone Simulator/5.1/Applications/是否已删除我重新构建并再次启动
如预期的那样,将sqlite文件复制到〜/Library/Application Support/iPhone Simulator/5.1/Applications//Documents

但是,文件的大小明显小于捆绑包中的文件!
另外,使用这样的谓词进行简单查询= [NSPredicate predicateWithFormat:@“string1 ==%@”,string1];清楚地表明string1不再被索引

之后,我创建了新版本的数据模型,并进行了无意义的更新,以强制进行轻量级迁移
如果在模拟器上运行,则迁移将花费几秒钟,数据库的大小将增加一倍,并且同一查询现在只需不到一秒钟的时间即可返回,而无需花费几分钟。
这可以解决我的问题,强制进行迁移,但是相同的迁移在iPad上需要3分钟,并且发生在前台。
因此,这就是我现在所处的位置,对我来说,最好的解决方案仍然是防止删除索引,在启动时任何其他导入解决方案都需要花费太多时间。
如果您需要更多说明,请告诉我...

更新2
因此,到目前为止,我获得的最好结果是使用由具有类似数据模型的快速工具生成的sqlite文件播种核心数据数据库,但在生成sqlite文件时未设置索引。然后,我将带有索引集的sqlite文件导入核心数据应用程序,并进行轻量级迁移。对于新iPad上的200万条记录,此迁移过程需要3分钟。最终应用程序的记录数应是此记录数的5倍,因此我们仍在等待很长的处理时间。
如果我走这条路,新的问题将是:可以在后台执行轻量级迁移吗?

更新
我的问题不是如何创建工具来填充Core Data数据库,然后将sqlite文件导入到我的应用中。我知道该怎么做,我已经做过无数次了。但是直到现在,我还没有意识到这种方法可能会产生一些副作用:就我而言,以这种方式导入sqlite文件时,结果数据库中的索引属性显然变得“未索引”。
如果您能够验证在进行此类传输后仍对任何索引数据进行索引,那么我很想知道您如何进行操作,否则,什么是有效播种此类数据库的最佳策略是什么。

原始

我有一个大型CSV文件(数百万行),其中包含4列,字符串和浮点数。
这是针对iOS应用的。
我需要在首次加载应用程序时将其加载到核心数据中。
在数据可用之前,该应用程序几乎无法运行,因此加载时间很重要,因为首次用户显然不希望该应用程序花费20分钟才能运行。
现在,我当前的代码在新iPad上需要20分钟才能处理200万行的csv文件。
我正在使用后台上下文来不锁定UI,而是每1000条记录保存一次上下文
我最初的想法是在模拟器上生成数据库,然后在首次启动时将其复制/粘贴到文档文件夹中,因为这是播种大型数据库的常见的非官方方式。不幸的是,索引似乎无法在这种传输中幸免,尽管数据库仅在几秒钟后就可用,但是性能却很糟糕,因为我的索引丢失了。我已经发布了有关索引的问题,但是似乎并没有很好的答案。

所以我要寻找的是:
  • 一种提高在核心数据中加载数百万条记录的性能的方法
  • (如果数据库在首次启动时已预先加载并移动),这是保持索引
  • 的一种方法
  • 处理此类情况的最佳实践。我不记得使用过任何需要我等待x分钟才能首次使用的应用程序(但也许是The Daily,这真是糟糕的体验)。
  • 任何使用户没有意识到的创新方法:在进行教程时进行背景导入等...
  • 不使用核心数据吗?
  • ...
  • 最佳答案

    使用以Cocoa编写的脱机应用程序(例如,命令行实用程序)预生成数据库,该应用程序在OS X上运行,并使用与iOS使用的相同的Core Data框架。您无需担心“索引存活”之类的问题-输出是由Core Data生成的.sqlite数据库文件,可直接由iOS应用立即使用。

    只要您可以离线进行数据库生成,就算是目前最好的解决方案。我已经成功地使用了这种技术来预生成用于iOS部署的数据库。查看我以前的问题/答案,以获取更多详细信息。

    关于ios - 将大型CSV文件加载到核心数据中最快的方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10443936/

    相关文章:

    objective-c - [self self] 在 objective c 中是什么意思?

    ios - coreData Fetch 中的 NSInternalInconsistencyException

    ios - NSFetchRequest 上的 Swift2.0 CoreData 问题

    ios - 类别中的 NSManagedObject 子类属性

    ios - 使用 Xcode 7.2 录制时 XCUITest 崩溃

    ios - Xamarin iOS : Can we set stoardboardid in XIB file?

    iphone - 给定一个 CGPath,如何让它弯曲?

    sql - SQLite)在图结构上查询以获取和获取链接

    iphone - Sqlite 数据检索问题

    python - Sqlalchemy 在时隙中获取行