6

結論
問題は解決したと思います。
問題は方法論とは何の関係もなかったようですが、XCodeはビルドの合間にプロジェクトを正しくクリーンアップしませんでした。
これらすべてのテストの後、使用されていたsqliteファイルはまだインデックス付けされていない最初のファイルだったようです......
XCode 4.3.2に注意してください、私はCleanがクリーニングされない、またはプロジェクトにファイルを追加しても、バンドルリソースに自動的に追加されません...
さまざまな回答をありがとうございます。

更新3
誰もが同じ手順を試して同じ結果が得られるかどうかを確認するように勧めているので、私が行ったことを詳しく説明します。
空白のプロジェクトから始めて
、1つのエンティティ、3つの属性(2つの文字列、1つのフロート)でデータモデルを定義しました。
最初の文字列はインデックス付けされて
ここに画像の説明を入力してください

います。didfinishLaunchingWithOptionsで、次のように呼び出しています。

[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 / ApplicationSupport/にコピーされます。 iPhoneシミュレーター/5.1/アプリケーション//ドキュメント

ただし、ファイルのサイズはバンドルよりも大幅に小さくなっています。 また、この述語= [NSPredicate predicateWithFormat:@ "string1 ==%@"、string1];のような述語を使用して単純なクエリを実行します。string1がインデックスに登録されていないことを明確に示しています

。その後、軽量の移行を強制するために、意味のない更新を含む新しいバージョンのデータモデルを作成します
。シミュレータで実行すると、移行に数秒かかり、データベースのサイズが2倍になります。同じクエリが返されるのに数分ではなく1秒もかからないようになりました。
これは私の問題を解決し、移行を強制しますが、同じ移行はiPadで3分かかり、フォアグラウンドで行われます。
ですから、私が今いるところです。私にとって最善の解決策は、インデックスが削除されないようにすることです。起動時に他のインポートソリューションは時間がかかりすぎるだけです。
さらに詳しい説明が必要な場合はお知らせください...

アップデート2
これまでのところ、私がこれまでに得た最良の結果は、同様のデータモデルを備えたクイックツールから生成されたsqliteファイルをコアデータデータベースにシードすることですが、sqliteファイルを生成するときにインデックスは設定されていません。次に、このsqliteファイルをインデックスが設定されたコアデータアプリにインポートし、軽量の移行を可能にします。新しいiPadでの200万件の記録の場合、この移行にはまだ3分かかります。最終的なアプリにはこの数の5倍のレコードが含まれているはずなので、まだ長い長い処理時間を検討しています。そのルートに行くと、新しい質問は次のようになります。バックグラウンドで軽量の移行を実行できますか?

更新
私の質問は、Core Dataデータベースにデータを取り込むためのツールを作成し、sqliteファイルをアプリにインポートする方法ではありません。
私はこれを行う方法を知っています、私はそれを数え切れないほどしました。
しかし今まで、そのようなメソッドが何らかの副作用をもたらす可能性があることに気づいていませんでした。私の場合、sqliteファイルをそのようにインポートすると、結果のデータベースのインデックス付き属性が明らかに「インデックスなし」になりました。
インデックス付けされたデータがそのような転送後もインデックス化されていることを確認できた場合は、どのように進めるか、またはそのようなデータベースを効率的にシードするための最良の戦略は何かを知りたいと思います。

オリジナル

4列、文字列、浮動小数点数の大きなCSVファイル(数百万行)があります。これはiOSアプリ用です。

アプリを初めてロードするときに、これをコアデータにロードする必要があります。

データが利用可能になるまでアプリはほとんど機能しないため、読み込み時間が重要です。初めてのユーザーは、アプリを実行できるようになるまでにアプリの読み込みに20分かかることを望んでいないことは明らかです。

現在、私の現在のコードは、新しいiPadで200万行のcsvファイルを処理するのに20分かかります。

UIをロックせず、1,000レコードごとにコンテキストを保存するためにバックグラウンドコンテキストを使用しています

私が最初に考えたのは、シミュレーターでデータベースを生成し、最初の起動時にそれをコピーしてドキュメントフォルダーに貼り付けることでした。これは、大規模なデータベースをシードする一般的な非公式の方法です。残念ながら、インデックスはそのような転送に耐えられないようです。データベースはほんの数秒で利用可能になりましたが、インデックスが失われたため、パフォーマンスはひどいものになりました。すでにインデックスについての質問を投稿しましたが、それに対する良い答えはないようです。

だから私が探しているのは、次のいずれかです。

  • コアデータに数百万のレコードをロードする際のパフォーマンスを向上させる方法
  • データベースが最初の起動時にプリロードされて移動された場合、インデックスを保持する方法
  • この種のシナリオを処理するためのベストプラクティス。最初に使用する前にx分待つ必要のあるアプリを使用したことを覚えていません(ただし、おそらくThe Dailyであり、それはひどい経験でした)。
  • ユーザーが気付かないうちに待たせるためのクリエイティブな方法:チュートリアル中のバックグラウンドインポートなど...
  • コアデータを使用していませんか?
  • ..。
4

2 に答える 2

6

OS X 上で動作し、iOS が使用するのと同じ Core Data フレームワークを使用する、Cocoa で記述されたオフライン アプリケーション (コマンドライン ユーティリティなど) を使用して、データベースを事前に生成します。「存続するインデックス」などについて心配する必要はありません。出力は Core Data によって生成された .sqlite データベース ファイルであり、iOS アプリで直接かつすぐに使用できます。

DB 生成をオフラインで実行できる限り、これが最良のソリューションです。私はこの手法を使用して、iOS 展開用のデータベースを事前に生成しました。もう少し詳細については、以前の質問/回答を確認してください。

于 2012-05-04T06:43:56.217 に答える
0

I'm just starting out with SQLite and I need to integrate a DB into one of my apps that will have a lot of indexed data in a SQLite database. I was hoping I could do some method where I could bulk insert my information into a SQLite file and add that file to my project. After discovering and reading through your question, the provided answer and the numerous comments, I decided to check out the SQLite source to see if I could make heads or tails of this issue.

My initial thought was that the iOS implementation of SQLite is, in fact, throwing out your indices. The reason is because you initially create your DB index on x86/x64 system. The iOS is an ARM processor, and numbers are handled differently. If you want your indexes to be fast, you should generate them in such a way that they are optimized for the processor in which they will be searched.

Since SQLite is for multiple platforms, it would make since to drop any indices that have been created in another architecture and rebuild them. However, since no one wants to wait for an index to rebuild the first time it is accessed, the SQLite devs most likely decided to just drop the index.

After digging into the SQLite code, I've come to the conclusion that this is most likely happening. If not for the processor architecture reason, I did find code (see analyze.c and other meta-information in sqliteint.h) where indices were being deleted if they were generated under an unexpected context. My hunch is that the context that drives this process is how the underlying b-tree data structure was constructed for the existing key. If the current instance of SQLite can't consume the key, it deletes it.

It is worth mentioning that the iOS Simulator is just that-- a simulator. It is not an emulator of the, hardware. As such, your app is running in a pseudo-iOS device, running on an x86/x64 processor.

When your app and SQLite DB are loaded to your iOS device, an ARM-compiled variant is loaded, which also links to the ARM compiled libraries within iOS. I couldn't find ARM specific code associated with SQLite, so I imagine Apple had to modify it to their suit. The could also be part of the problem. This may not be an issue with the root-SQLite code, it could be an issue with the Apple/ARM compiled variant.

The only reasonable solution that I can come up with is that you can create a generator application that you run on your iOS machine. Run the application, build the keys, and then rip the SQLite file from the device. I'd imagine such a file would work across all devices, since all ARM processors used by iOS are 32-bit.

Again, this answer is a bit of an educated guess. I'm going to re-tag your question as SQLite. Hopefully a guru may find this and be able to weigh in on this issue. I'd really like to know the truth for my own benefit.

于 2013-01-29T13:29:59.527 に答える