Wniosek
Problem chyba zamknięty.
Wygląda na to, że problem nie miał nic wspólnego z metodologią, ale że XCode nie wyczyścił poprawnie projektu pomiędzy wersjami. Wygląda na to, że po tych wszystkich testach plik sqlite, który był używany był wciąż pierwszym, który nie był indeksowany ......
Uważaj na XCode 4.3.2, mam tylko problemy z Clean nie czyszczenia lub dodawanie plików do projektu nie są automatycznie dodawane do środków wiązek ...
Dzięki dla różnych odpowiedzi ..Jaki jest najszybszy sposób na załadowanie dużego pliku CSV do danych podstawowych
Update 3
Odkąd zaprosić kogoś po prostu wypróbować te same kroki, aby zobaczyć jeśli uzyskają takie same wyniki, pozwól mi szczegółowo opisać, co zrobiłem:
Zacznę od pustego projektu
ja zdefiniował datamodel z jednego podmiotu, 3 atrybutów (2 ciągi, 1 float)
Pierwszy ciąg jest indeksowana
W nie finishLaunchingWithOptions wzywam:
[self performSelectorInBackground:@selector(populateDB) withObject:nil];
Kod dla populateDb jest poniżej:
-(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");
}
Cała reszta to domyślny kod danych podstawowych, nic nie dodano.
Uruchomiłem to w symulatorze.
idę do ~/Library/Application Support/iPhone Simulator/5.1/Aplikacje // Documents
znajduje się plik SQLite, który jest generowany
Biorę to i skopiować go w moim pakiecie
I komentarz na wezwanie do populateDb
edytować persistentStoreCoordinator skopiować pliku sqlite od wiązki do dokumentów przy pierwszym uruchomieniu
- (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;
}
}
Usuwam aplikację z symulatora, sprawdzam, czy ~/Library/Application Support/iPhone Simulator/5.1/Applications/jest teraz usunięty
Przebudowuję i ponownie uruchamiam
Zgodnie z oczekiwaniami plik sqlite jest kopiowany do ~/Library/Application Support/iPhone Simulator/5.1/Applications // Documents
Jednak rozmiar pliku jest mniejszy niż w pakiecie, znacznie! Wykonuje również proste zapytanie z predykatem takim jak ten predykat = [NSPredicate predicateWithFormat: @ "string1 ==% @", string1]; wyraźnie pokazuje, że łańcuch1 nie jest indeksowana już
obserwuję, że tworzę nową wersję datamodel, z bezsensownego aktualizacji, tak aby wymusić lekką migrację
Jeśli uruchamianie na symulatorze, migracja trwa kilka sekundy, baza danych podwaja się, a to samo zapytanie zajmuje teraz mniej niż sekundę, aby powrócić zamiast minut.
Rozwiązałoby to mój problem, wymuszanie migracji, ale ta sama migracja trwa 3 minuty na iPadzie i dzieje się na pierwszym planie.
W tej chwili jestem na miejscu, najlepszym rozwiązaniem dla mnie będzie nadal zapobieganie usuwaniu indeksów, każde inne rozwiązanie importowania w momencie uruchomienia zajmuje po prostu zbyt dużo czasu.
Daj mi znać, jeśli potrzebujesz więcej wyjaśnień ...
Aktualizacja 2
Więc najlepszym wynikiem miałem tak daleko jest do zainicjowania bazy danych rdzeń z pliku sqlite wytwarzanej z szybkiego narzędzia z podobnym model danych, ale bez indeksów ustawionych podczas tworzenia pliku sqlite. Następnie importuję ten plik sqlite do głównej aplikacji danych z ustawionymi zestawami indeksów i zezwalam na niewielką migrację. Za 2 miliony nagrań na nowym iPadzie, ta migracja trwa 3 minuty. Ostateczna aplikacja powinna mieć 5-krotność tej liczby rekordów, więc wciąż szukamy długiego czasu przetwarzania. Jeśli pójdę tą drogą, nowe pytanie brzmi: czy lekka migracja może być przeprowadzona w tle?
Aktualizacja
moje pytanie nie jest, jak stworzyć narzędzie do wypełnienia bazy danych Core, a następnie zaimportować plik sqlite w mojej aplikacji.
Wiem, jak to zrobić, zrobiłem to niezliczoną ilość razy.
Ale do tej pory nie zdawałem sobie sprawy, że taka metoda może mieć pewne skutki uboczne: w moim przypadku indeksowany atrybut w wynikowej bazie danych wyraźnie "cofnął indeks" podczas importowania pliku sqlite w ten sposób.
Jeśli byłeś w stanie zweryfikować, czy jakiekolwiek indeksowane dane są nadal indeksowane po takim przeniesieniu, chciałbym wiedzieć, jak postępować, lub w inny sposób, jaka byłaby najlepsza strategia efektywnego rozmieszczenia takiej bazy danych.
Original
Mam dużego pliku CSV (mln linii) z 4 kolumn, smyczki i pływaków. To jest dla aplikacji na iOS.
Potrzebuję tego, aby załadować do podstawowych danych przy pierwszym załadowaniu aplikacji.
Aplikacja jest praktycznie nieczynna, dopóki dane nie będą dostępne, więc czas ładowania ma znaczenie, ponieważ użytkownik po raz pierwszy oczywiście nie chce, aby aplikacja ładowała się 20 minut, zanim będzie mogła go uruchomić.
W tej chwili mój obecny kod zajmuje 20 minut na nowym iPadzie, aby przetworzyć plik csv o wartości 2 milionów wierszy.
Używam kontekst tła aby nie zablokować UI i zapisać kontekście każde 1000 rekordów
Pierwszym pomysłem było to, aby wygenerować bazę danych na symulatorze, a następnie skopiować/wkleić go w folderze dokumentu na pierwsze uruchomienie, ponieważ jest to powszechny nieoficjalny sposób zasiania dużej bazy danych. Niestety, wydaje się, że indeksy nie przetrwały takiego transferu i chociaż baza danych była dostępna już po kilku sekundach, wydajność jest straszna, ponieważ moje indeksy zostały utracone. Już napisałem pytanie o indeksy, ale nie wydaje się, aby była to dobra odpowiedź.
Więc co szukam, albo:
- sposób, aby poprawić wydajność przy załadunku miliony rekordów w danych podstawowych
- jeśli baza danych jest wstępnie załadowany i przeniósł się na pierwszym uruchomieniu drogę zachować moje indeksy
- najlepsze praktyki dotyczące obsługi tego rodzaju scenariusza.Nie pamiętam, aby użyć jakiejkolwiek aplikacji, która wymaga ode mnie czekania na x minut przed pierwszym użyciem (ale może na Daily, a to było straszne doświadczenie).
- Dowolny twórczy sposób, aby sprawić, by użytkownik poczekał, nie zdając sobie z tego sprawy: import w tle podczas przechodzenia przez samouczek, itd ...
- Nie używając danych podstawowych?
- ...
W jaki sposób skończyłeś "oczyścić" projekt, aby działał poprawnie? – lnafziger
Clean nie działa, ale ponowne uruchomienie laptopa, ręczne czyszczenie wszystkich odniesień do pliku itp., Wydaje się, że "rozwiązał" problem. Dziwne ... chociaż musiałem też usunąć lekkie linie migracji, aby wymusić migrację (ponieważ zajęłoby to wiele minut). Ogólnie rzecz biorąc, nie jest to czysta implementacja, na którą mam nadzieję, ale działa ... dopóki wersja 2 nie wymaga aktualizacji modelu danych, wtedy mam kłopoty. –